[GHC] #9883: Make OverloadedLists more usable by splitting the class interface

GHC ghc-devs at haskell.org
Sat Dec 13 13:50:25 UTC 2014


#9883: Make OverloadedLists more usable by splitting the class interface
-------------------------------------+-------------------------------------
       Reporter:  muesli4            |                   Owner:
           Type:  feature request    |                  Status:  new
       Priority:  normal             |               Milestone:  ⊥
      Component:  External Core      |                 Version:  7.8.3
       Keywords:  overloaded lists,  |        Operating System:
  islist                             |  Unknown/Multiple
   Architecture:  Unknown/Multiple   |         Type of failure:
     Difficulty:  Moderate (less     |  None/Unknown
  than a day)                        |               Test Case:
     Blocked By:                     |                Blocking:
Related Tickets:                     |  Differential Revisions:
-------------------------------------+-------------------------------------
 == 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.

--
Ticket URL: <http://ghc.haskell.org/trac/ghc/ticket/9883>
GHC <http://www.haskell.org/ghc/>
The Glasgow Haskell Compiler


More information about the ghc-tickets mailing list