Difference between revisions of "Multiple instances"
m (type parameter order) |
(→Answer) |
||
(One intermediate revision by one other user not shown) | |||
Line 2: | Line 2: | ||
I like to define multiple [[type class instance]]s for the same pair of class and type. |
I like to define multiple [[type class instance]]s for the same pair of class and type. |
||
+ | Sometimes I also need two instances where the order of type parameters is different. |
||
+ | E.g. I want to define two <hask>Functor</hask> instances for a pair: |
||
+ | One instance where the first member is mapped and another instance where the second member is mapped. |
||
How is it possible? |
How is it possible? |
||
+ | |||
== Answer == |
== Answer == |
||
− | You can |
+ | You can define multiple type class instances for the same pair of class and type if you keep the class and type definitions in different modules, i.e. not the modules than contains the instance declarations. |
− | These instances are |
+ | These instances are therefore called [[orphan instance]]s. |
+ | |||
− | However you won't be lucky with this solution, since you must ensure, |
||
− | that two modules with conflicting instances declarations are never imported together |
+ | However this isn't perfect, since you must ensure that two modules with conflicting instances declarations are never imported together because instance declarations are automatically imported and cannot be hidden. |
+ | |||
− | since instance declarations are automatically imported and cannot be hidden. |
||
− | + | Furthermore, modules which import conflicting modules only indirectly conflict itself. |
|
− | It is also not possible to define instances with respect to different type parameter orders, |
||
− | as it is required for type constructor classes like <hask>Functor</hask>, <hask>Monad</hask>, and others. |
||
− | Thus multiple instances should be avoided, and a safe way to do this is to avoid orphan instances. |
+ | Thus multiple instances should be avoided, and a safe way to do this is to avoid orphan instances. You can achieve this by wrapping the type in a <hask>newtype</hask> and lift all required instances to that new type. |
− | You can achieve this by wrapping the type in a <hask>newtype</hask> and lift all required instances to that new type. |
||
If you do not fear [[Use of language extensions|language extensions]] you can simplify this task considerably using the <code>GeneralizedNewtypeDeriving</code> feature. |
If you do not fear [[Use of language extensions|language extensions]] you can simplify this task considerably using the <code>GeneralizedNewtypeDeriving</code> feature. |
||
− | The custom instance can be defined for the class/newtype pair and it is not orphan |
+ | The custom instance can be defined for the class/newtype pair and it is not orphan if it is defined where newtype is introduced. |
+ | Using <hask>newtype</hask> you can also change the order of type parameters or give type parameters a fixed type. |
||
+ | Example: |
||
+ | There are so many types and operations that exhibit a monoid structure, |
||
+ | but it would not be useful to call all the operations <hask>mempty</hask> and <hask>mappend</hask>. |
||
+ | It is however useful to call them via the Monoid interface sometimes, e.g. in the <hask>Writer</hask> monad. |
||
+ | Thus the module <hask>Data.Monoid</hask> provides several newtype wrappers for common monoids. |
||
== See also == |
== See also == |
Latest revision as of 08:58, 30 November 2016
Question
I like to define multiple type class instances for the same pair of class and type.
Sometimes I also need two instances where the order of type parameters is different.
E.g. I want to define two Functor
instances for a pair:
One instance where the first member is mapped and another instance where the second member is mapped.
How is it possible?
Answer
You can define multiple type class instances for the same pair of class and type if you keep the class and type definitions in different modules, i.e. not the modules than contains the instance declarations. These instances are therefore called orphan instances.
However this isn't perfect, since you must ensure that two modules with conflicting instances declarations are never imported together because instance declarations are automatically imported and cannot be hidden.
Furthermore, modules which import conflicting modules only indirectly conflict itself.
Thus multiple instances should be avoided, and a safe way to do this is to avoid orphan instances. You can achieve this by wrapping the type in a newtype
and lift all required instances to that new type.
If you do not fear language extensions you can simplify this task considerably using the GeneralizedNewtypeDeriving
feature.
The custom instance can be defined for the class/newtype pair and it is not orphan if it is defined where newtype is introduced.
Using newtype
you can also change the order of type parameters or give type parameters a fixed type.
Example:
There are so many types and operations that exhibit a monoid structure,
but it would not be useful to call all the operations mempty
and mappend
.
It is however useful to call them via the Monoid interface sometimes, e.g. in the Writer
monad.
Thus the module Data.Monoid
provides several newtype wrappers for common monoids.