Variadic arguments, using Type Families instead of Multi-param Typeclasses
-
22-02-2021 - |
Question
Here's what I've got, expressed with MultiParamTypeClasses:
class ListResultMult r a where
lstM :: a -> [a] -> r
listM :: ListResultMult r a => a -> r
listM a = lstM a []
instance ListResultMult r a => ListResultMult (a -> r) a where
lstM a as x = lstM x $ a:as
instance ListResultMult [a] a where
lstM a as = reverse $ a:as
instance Show a => ListResultMult (IO ()) a where
lstM a as = print . reverse $ a:as
Here's what I tried, using TypeFamilies (TypeSynonymInstances didn't help):
class ListResultFam r where
type Elem r :: *
lstF :: Elem r -> [Elem r] -> r
listFam :: ListResultFam r => Elem r -> r
listFam a = lstF a []
-- Illegal type synonym family application in instance: Elem r -> r
-- in the instance declaration for `ListResultFam (Elem r -> r)'
instance ListResultFam r => ListResultFam (Elem r -> r) where
type Elem (Elem r -> r) = Elem r
lstF a as x = lstF x $ a:as
instance ListResultFam [a] where
type Elem [a] = a
lstF a as = reverse $ a:as
Is there any way to accomplish this with Type Families? Why is this an "illegal" type synonym family application?
Solution
Type families aren't a replacement for multi param type classes, rather they replace functional dependencies.
This is because type families allow a mapping from one type to another, which is similar to what functional dependencies do with multi param type classes. Ie:
class Collection col e | col -> e where
getHead :: col -> e
instance Collection [a] a where
getHead = head
can be represented with type families as:
class Collection col where
type CollectionHead col :: *
getHead :: col -> CollectionHead col
instance Collection [a] where
type CollectionHead [a] = a
getHead = head
However they cannot replace a multi param type class without functional dependencies. Eg
class Converter a b where
convert :: a -> b
instance Converter Int String where
convert = show
instance Converter Int [Int] where
convert x = [x]
Cannot be done by removing the b param and using type families. You could do something like this:
class Converter a where
type Target a :: *
convert :: a -> Target a
instance Converter Int where
type Target Int = String
convert = show
However it isn't possible to write the second instance, as it require a duplicate instance Converter Int
.
With regards to your program, you can see instantly that there are no functional dependencies, just multiple parameter type classes. You therefor cannot do a straight conversion to type families with this.