class Listy a b where
fromList :: [b] -> a
toList :: a -> [b]
lifted :: ([b] -> [b]) -> (a -> a)
lifted f = fromList . f . toList
data MyString = MyString { getString :: String } deriving Show
instance Listy MyString Char where
toList = getString
fromList = MyString
Now I need to write e.g. lifted (reverse::(String -> String)) (MyString "Foobar"). Is there a trick to avoid the need for the type signatures?
Essentially the problem is that setting the type of
adoes not tell the compiler what the type ofbis. You might think that since there is only one instance of the class (whereaisMyStringandbisChar), but anybody can add new instances at any time. So the fact that there’s only one instance now doesn’t help the compiler decide what types you want.The solution to this is either to use Functional Dependencies, or Type Families. The latter is the newer solution and is intended to eventually “replace” the former, but right now both are still fully supported. Whether FDs ever go away remains to be seen. Anyway, with FDs:
Essentially this says “there can only ever be one class instance for each
a“. In other words, once you knowa, you can always determineb. (But not the reverse.) The rest of the class looks like it did before.The alternative is TFs:
Now instead of the second type being called
b, it’s calledElement a. The wordElementacts like a class method that takes a listy type and returns the corresponding element type. You can then doand so on. (Not that
Listynecessarily makes sense for all the types above; these are just examples of how to define the class.)