Я играл с TypeFamilies
, FunctionalDependencies
и MultiParamTypeClasses
. И мне кажется, что TypeFamilies
не добавляет каких-либо конкретных функций по сравнению с двумя другими. (Но не наоборот). Но я знаю, что семейства типов очень нравятся, поэтому я чувствую, что у меня что-то не хватает:Что может типа семейств, что классы множественного типа и функциональные зависимости не могут
«открытое» отношение между типами, такое как функция преобразования, которая не представляется возможной с TypeFamilies
. Совершенно с MultiParamTypeClasses
:
class Convert a b where
convert :: a -> b
instance Convert Foo Bar where
convert = foo2Bar
instance Convert Foo Baz where
convert = foo2Baz
instance Convert Bar Baz where
convert = bar2Baz
Сюръективная соотношение между типами, такими как своего родом типом безопасного псевдо-утком механизма типизации, которая обычно делаются с помощью стандартного типа семьи. Совершенно с MultiParamTypeClasses
и FunctionalDependencies
:
class HasLength a b | a -> b where
getLength :: a -> b
instance HasLength [a] Int where
getLength = length
instance HasLength (Set a) Int where
getLength = S.size
instance HasLength Event DateDiff where
getLength = dateDiff (start event) (end event)
Биективных отношениями между типами, такими как для Unboxed контейнера, который может быть сделан через TypeFamilies
с семьей данных, хотя тогда вы должны объявить новый тип данных для каждого содержащегося типа , например, с newtype
. Либо это, либо с инъективного типа семьи, который я думаю, не доступен до GHC 8. Done с MultiParamTypeClasses
и FunctionalDependencies
:
class Unboxed a b | a -> b, b -> a where
toList :: a -> [b]
fromList :: [b] -> a
instance Unboxed FooVector Foo where
toList = fooVector2List
fromList = list2FooVector
instance Unboxed BarVector Bar where
toList = barVector2List
fromList = list2BarVector
И, наконец Сюръективный отношений между двумя типами и третьего типа, такие как python2 или java style функция разделения, которая может быть выполнена с помощью TypeFamilies
, также используя MultiParamTypeClasses
. Совершено с MultiParamTypeClasses
и FunctionalDependencies
:
class Divide a b c | a b -> c where
divide :: a -> b -> c
instance Divide Int Int Int where
divide = div
instance Divide Int Double Double where
divide = (/) . fromIntegral
instance Divide Double Int Double where
divide = (. fromIntegral) . (/)
instance Divide Double Double Double where
divide = (/)
Еще одна вещь, которую я должен также добавить, что кажется, FunctionalDependencies
и MultiParamTypeClasses
также весьма немного более кратким (для приведенных выше в любом случае примеров), как у вас есть только написать тип один раз, и вы не должны придумать имя фиктивного типа, который затем вы должны ввести для каждого экземпляра, как вы делаете с TypeFamilies
:
instance FooBar LongTypeName LongerTypeName where
FooBarResult LongTypeName LongerTypeName = LongestTypeName
fooBar = someFunction
против:
instance FooBar LongTypeName LongerTypeName LongestTypeName where
fooBar = someFunction
Поэтому, если я не убежден в этом, мне кажется, что я не должен беспокоиться о TypeFamilies
и использовать исключительно FunctionalDependencies
и MultiParamTypeClasses
. Поскольку, насколько я могу судить, это сделает мой код более кратким, более последовательным (одно меньшее расширение, чтобы заботиться), а также даст мне большую гибкость, например, с отношениями открытого типа или биективными отношениями (возможно, последний является решателем GHC 8).
Типовые семейства обычно работают намного лучше, чем фонды, особенно при реализации функций типа. – ErikR
Существует также вопрос читаемости. Эквивалент типа F a = G (H (G a) (G a)), выраженный через FunDeps, требует нескольких ограничений, связанных с несколькими вспомогательными переменными типа. Когда я читал такие ограничения, я обнаружил, что пытаюсь выразить их в функциональной форме. Возможно, это потому, что я больше привык читать функциональный код, чем пролог. – chi
@ErikR Почему? Это кажется невероятно странным, потому что примеры, в которых оба варианта жизнеспособны, выглядят полностью идентичными с точки зрения разрешения типа и не для меня. Это просто недостаток в текущей реализации «Функциональныхзависимостей» или «MultiParamTypeClasses» или их взаимодействия? Или это нечто более фундаментальное? Я действительно надеюсь на первое, так как было бы неплохо не использовать другой и, казалось бы, более подробный синтаксис, половину времени для одной и той же вещи только по соображениям производительности. – semicolon