Difference between revisions of "List instance"

From HaskellWiki
Jump to navigation Jump to search
(→‎Question: Haskell 98 -> Haskell 2010)
(2 intermediate revisions by 2 users not shown)
Line 1: Line 1:
 
== Question ==
 
== Question ==
   
How to make a list type an instance of some type class in Haskell 98?
+
How to make a list type an instance of some type class in Haskell 2010?
Haskell 98 does not support instances on particular composed types like <hask>String</hask>.
+
Haskell 2010 does not support instances on particular composed types like <hask>String</hask>.
   
   
 
If I have a type class for conversion to a type <hask>X</hask>:
 
If I have a type class for conversion to a type <hask>X</hask>:
   
<haskell>
+
<haskell>
 
class C a where
 
class C a where
 
toX :: a -> X
 
toX :: a -> X
Line 35: Line 35:
 
</code>
 
</code>
 
 
Is there some type class cleverness that can make this work in haskell
+
Is there some type class cleverness that can make this work in Haskell 98?
98? I can create a new wrapper type for strings:
 
   
 
I'm aware of the approach taken by class <hask>Show</hask> in the prelude,
<haskell>
 
 
which adds a extra method to the class:
newtype StringWrap = StringWrap String
 
</haskell>
 
 
and write an instance for that, but then I'll have to litter my code
 
with calls to this constructor.
 
 
I'm aware of the approach taken by class <hask>Show</hask> in the prelude, which
 
adds a extra method to the class:
 
 
 
<haskell>
 
<haskell>
 
class C a where
 
class C a where
Line 59: Line 50:
 
== Answer ==
 
== Answer ==
   
  +
First you have to find out, what you really want to do.
The trick in the Prelude is that <hask>listToX</hask> has a default implementation.
 
   
  +
=== Define a custom type ===
If this is not possible in your application then introduce a new class like
 
   
  +
If you do not need list functions, but e.g. just want to use <hask>String</hask> values as identifiers,
  +
then I advise to just define a custom type:
 
<haskell>
 
newtype Identifier = Identifier String
 
</haskell>
  +
  +
and write the instances you need for that type.
  +
  +
=== Keep working with the list (or container) type ===
  +
  +
If you need list functions,
  +
then the wrapper approach means that you have to litter your code with calls to the wrapper constructor.
  +
In this case you should stick to the original composed type.
  +
The trick in the Prelude for the <hask>Show</hask> class is,
 
that <hask>listToX</hask> has a default implementation.
  +
 
If this is not possible in your application then introduce a new class like
 
<haskell>
 
<haskell>
 
class Element a where
 
class Element a where
 
listToX :: [a] -> X
 
listToX :: [a] -> X
 
</haskell>
 
</haskell>
 
 
and define instances like
 
and define instances like
 
 
<haskell>
 
<haskell>
 
instance Element Char where
 
instance Element Char where
Line 79: Line 85:
 
.
 
.
   
 
More generally, you can introduce a new class like
----
 
Or more generally, you can introduce a new class like
 
 
 
<haskell>
 
<haskell>
 
class IsChar a where
 
class IsChar a where
Line 90: Line 94:
 
toChar = id
 
toChar = id
 
</haskell>
 
</haskell>
 
and then whenever you want to use the "<hask>a</hask>" type as a <hask>Char</hask>,
  +
you just convert it from or to <hask>Char</hask> with <hask>fromChar</hask> or <hask>toChar</hask>, respectively.
   
 
=== FlexibleInstances ===
and then whenever you want to use the "<hask>a</hask>" type as a <hask>Char</hask>, you just convert it from/to <hask>Char</hask> with <hask>fromChar</hask>/<hask>toChar</hask> as appropriate.
 
   
  +
If none of the above solution is appropriate for you,
== With FlexibleInstances ==
 
  +
then you may choose the <code>FlexibleInstances</code> language extension
The <hask>-XFlexibleInstances</hask> language extension is designed to solve this problem. See http://www.haskell.org/ghc/docs/6.8-latest/html/users_guide/type-class-extensions.html#instance-rules
 
  +
that is available in GHC and Hugs.
 
See http://www.haskell.org/ghc/docs/6.8-latest/html/users_guide/type-class-extensions.html#instance-rules
   
 
<haskell>
 
<haskell>
 
{-# LANGUAGE FlexibleInstances #-}
 
{-# LANGUAGE FlexibleInstances #-}
instance C String where toX = ...
+
instance C [Char] where toX = ...
 
</haskell>
 
</haskell>
   

Revision as of 13:00, 14 December 2014

Question

How to make a list type an instance of some type class in Haskell 2010? Haskell 2010 does not support instances on particular composed types like String.


If I have a type class for conversion to a type X:

    class C a where
        toX   :: a -> X

I can define instances for

    instance C Int where toX  = ...
    instance C Double where toX  = ...
    instance C Tuple where toX  = ...

but not for Strings, given that they are a synonym for [Char]. Hence:

    instance C String where toX  = ...

results in:

   Illegal instance declaration for `C String'
       (The instance type must be of form (T a b c)
        where T is not a synonym, and a,b,c are distinct type variables)
   In the instance declaration for `C String'

Is there some type class cleverness that can make this work in Haskell 98?

I'm aware of the approach taken by class Show in the prelude, which adds a extra method to the class:

    class C a where
        toX     :: a -> X
        listToX :: [a] -> X

but I believe this says that whenever we can convert a to an X we can also convert [a] to an X, whereas I only want [Char] to be acceptable.

Answer

First you have to find out, what you really want to do.

Define a custom type

If you do not need list functions, but e.g. just want to use String values as identifiers, then I advise to just define a custom type:

    newtype Identifier = Identifier String

and write the instances you need for that type.

Keep working with the list (or container) type

If you need list functions, then the wrapper approach means that you have to litter your code with calls to the wrapper constructor. In this case you should stick to the original composed type. The trick in the Prelude for the Show class is, that listToX has a default implementation.

If this is not possible in your application then introduce a new class like

    class Element a where
        listToX :: [a] -> X

and define instances like

    instance Element Char where
        listToX = ...

    instance Element a => C [a] where
        toX = listToX

.

More generally, you can introduce a new class like

    class IsChar a where
        fromChar :: Char -> a
        toChar :: a -> Char
    instance IsChar Char where
        fromChar = id
        toChar = id

and then whenever you want to use the "a" type as a Char, you just convert it from or to Char with fromChar or toChar, respectively.

FlexibleInstances

If none of the above solution is appropriate for you, then you may choose the FlexibleInstances language extension that is available in GHC and Hugs. See http://www.haskell.org/ghc/docs/6.8-latest/html/users_guide/type-class-extensions.html#instance-rules

    {-# LANGUAGE FlexibleInstances #-}
    instance C [Char] where toX  = ...

Source

http://www.haskell.org/pipermail/haskell-cafe/2007-May/025742.html