Difference between revisions of "List instance"
(taken from Haskell-Cafe) |
(update doc link anyway) |
||
(11 intermediate revisions by 5 users not shown) | |||
Line 1: | Line 1: | ||
== Question == |
== Question == |
||
− | How to make a list type an instance of some type class in Haskell |
+ | How to make a list type an instance of some type class in Haskell 2010? |
− | Haskell |
+ | 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 X: |
+ | 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 |
||
− | </haskell> |
+ | </haskell> |
I can define instances for |
I can define instances for |
||
− | <haskell> |
+ | <haskell> |
instance C Int where toX = ... |
instance C Int where toX = ... |
||
instance C Double where toX = ... |
instance C Double where toX = ... |
||
instance C Tuple where toX = ... |
instance C Tuple where toX = ... |
||
− | </haskell> |
+ | </haskell> |
− | but not for Strings, given that they are a synonym for [Char]. Hence: |
+ | but not for Strings, given that they are a synonym for <hask>[Char]</hask>. Hence: |
− | <haskell> |
+ | <haskell> |
instance C String where toX = ... |
instance C String where toX = ... |
||
− | </haskell> |
+ | </haskell> |
results in: |
results in: |
||
Line 35: | Line 35: | ||
</code> |
</code> |
||
− | Is there some type class cleverness that can make this work in |
+ | Is there some type class cleverness that can make this work in Haskell 98? |
− | 98? I can create a new wrapper type for strings: |
||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
⚫ | |||
− | |||
− | and write an instance for that, but then I'll have to litter my code |
||
− | with calls to this constructor. |
||
− | |||
⚫ | |||
⚫ | |||
− | |||
<haskell> |
<haskell> |
||
class C a where |
class C a where |
||
toX :: a -> X |
toX :: a -> X |
||
listToX :: [a] -> X |
listToX :: [a] -> X |
||
− | </haskell> |
+ | </haskell> |
but I believe this says that whenever we can convert a to an <hask>X</hask> we can also |
but I believe this says that whenever we can convert a to an <hask>X</hask> we can also |
||
Line 59: | Line 50: | ||
== Answer == |
== 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 <hask>String</hask> values as identifiers, |
||
+ | then I advise to just define a custom type: |
||
⚫ | |||
⚫ | |||
⚫ | |||
+ | |||
+ | 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, |
||
⚫ | |||
+ | |||
⚫ | |||
<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 76: | Line 82: | ||
instance Element a => C [a] where |
instance Element a => C [a] where |
||
toX = listToX |
toX = listToX |
||
+ | </haskell> |
||
+ | . |
||
+ | |||
+ | More generally, you can introduce a new class like |
||
+ | <haskell> |
||
+ | class IsChar a where |
||
+ | fromChar :: Char -> a |
||
+ | toChar :: a -> Char |
||
+ | instance IsChar Char where |
||
+ | fromChar = id |
||
+ | toChar = id |
||
+ | </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 === |
||
+ | |||
+ | If none of the above solution is appropriate for you, |
||
+ | then you may choose the <code>FlexibleInstances</code> language extension |
||
+ | that is available in GHC and Hugs. |
||
+ | See [https://downloads.haskell.org/~ghc/8.0.2/docs/html/users_guide/glasgow_exts.html#instance-termination-rules instance rules]. |
||
+ | |||
+ | <haskell> |
||
+ | {-# LANGUAGE FlexibleInstances #-} |
||
+ | instance C [Char] where toX = ... |
||
</haskell> |
</haskell> |
||
Line 81: | Line 112: | ||
http://www.haskell.org/pipermail/haskell-cafe/2007-May/025742.html |
http://www.haskell.org/pipermail/haskell-cafe/2007-May/025742.html |
||
+ | |||
+ | [[Category:FAQ]] |
||
+ | [[Category:Idioms]] |
Latest revision as of 16:09, 22 March 2017
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 instance rules.
{-# LANGUAGE FlexibleInstances #-}
instance C [Char] where toX = ...
Source
http://www.haskell.org/pipermail/haskell-cafe/2007-May/025742.html