Skip to content

Make OverloadedLists more usable by splitting the class interface

Problem

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.

Proposal

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.

Another class

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.)

Using Data.Foldable

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.

Drawbacks

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:

  • IsList for overloaded list expressions
  • AsList or Foldable for 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.
Trac metadata
Trac field Value
Version 7.8.3
Type FeatureRequest
TypeOfFailure OtherFailure
Priority normal
Resolution Unresolved
Component External Core
Test case
Differential revisions
BlockedBy
Related
Blocking
CC
Operating system
Architecture
To upload designs, you'll need to enable LFS and have an admin enable hashed storage. More information