Make OverloadedLists more usable by splitting the class interface
While the OverloadedLists extension is very useful, it limits the types which can be used with it, by requesting too much. Assume you have a database specific DSEL which allows you to use list-like expressions in queries, it's easy to implement
fromList, but we are unable to implement
toList in a reasonable fashion without a backend and an existing connection.
Modify the class interface in a way that does not require the instance to be listable.
class IsList l where type Item l fromList :: [Item l] -> l fromListN :: Int -> [Item l] -> l
We could then provide the pattern matching functionality on
IsList instances with different approaches.
Just add another class which is used to provide the
toList function, used on pattern matches. This is the easiest approach
class AsList l where type Item l toList :: l -> [Item l]
Desugaring works as usual and it goes well with all structures. (The name is not the best though.)
The list pattern gets desugared using Data.Foldable:
f :: (IsList l, Foldable l) => l -> l f [x, y, z] = [x, y] f l = l
gets something like:
import Data.Foldable (toList) f :: (IsList l, Foldable l) => l -> l f (toList -> [x, y, z]) = fromList [x, y] f l = l
This approach does not go well with structures like
Data.Map, because it expects the type constructor to take the element type as first argument, but we would like to have a tuple type. Maybe a wrapper could be provided, but I think it's not the way to go, as long as Data.Foldable does not use type families.
Both approaches complicate the type of list expressions. This requires a bit more of typing, but it specifies exactly which functionality you need and one can simply drop the unused one, without creating dangerous dummy implementations:
IsListfor overloaded list expressions
Foldablefor pattern matching Most of the time OverloadedLists is used for convenience, so I don't expect the normal user to be really affected, library writers, specifically those who write some kind of DSEL, will have to be more precise, but get a more type-safe approach, which can not fail at runtime.