Reputation: 6333
I am new comer of Haskell and following is my question:
given this class:
class MyClass a where
foo :: a -> [a]
then I have a subclass that is more specific:
class (MyClass a) => SubClass a where
foo param = [bar param]
bar :: a -> a
but it doesn't work as expected. I was expecting a default implementation is setup in the definition of SubClass
but it doesn't. I will need to define the instance for MyClass
seperately, but that sounds stupid. How can I achieve default implementation when I know some subclass satisfies some property definitely?
More specifically, I want to express in Haskell, that when a class satisfies some properties, some functions for its parent can have default implementation
. In my example, SubClass
has property bar
such that I know foo
is definitely defined in such a way.
A more general form of this question is, is it a good idea to reuse by using classes and instances?
I found this post: Inclusion of typeclasses with default implementation in Haskell
It's quite close, but still not answering my question totally and their forms are little bit different.
Upvotes: 3
Views: 1813
Reputation: 2753
Elaborating on Daniel's answer:
Suppose you have a new datatype MyData
, defined as
data MyData = D1 | D2
You want to make MyData
an instance of SubClass
. You try the obvious solution first.
instance SubClass MyData where
bar x = case x of { D1 -> D2 ; D2 -> D1 }
A quick examination of the type signatures, though, reveals that this won't work, because a type has to be an instance of MyClass
before it can be an instance of SubClass
. So you make MyData
an instance of MyClass
.
instance MyClass MyData
Again, this raises an error, because foo
is included in the minimal complete definition of MyClass
. For your instance to work, you would have to manually define foo
, thus defeating the purpose of the default declaration.
In short, there is no way to do this in basic Haskell98 (or Haskell2010, for that matter). Thankfully, however, GHC provides a useful extension called DefaultSignatures
. So, using Daniel's example:
{-# LANGUAGE DefaultSignatures #-}
class MyClass a where
foo :: a -> [a]
default foo :: SubClass a => a -> [a]
foo param = [param]
class MyClass a => SubClass a where
bar :: a -> a
And now, you can define the instances and they will work as you would expect. The downside to this solution is that the default definition has to be defined in MyClass
, but this is necessary. The definition of foo
belongs to the definition of MyClass
(or one of its instance declarations), so if you were able to define the default declaration of foo
within the definition of SubClass
, Haskell's type isolation would be broken.
Upvotes: 6
Reputation: 152897
This can be achieved with DefaultSignatures
:
{-# LANGUAGE DefaultSignatures #-}
class MyClass a where
foo :: a -> [a]
default foo :: SubClass a => a -> [a]
foo param = [param]
class MyClass a => SubClass a where
bar :: a -> a
Testing it in ghci:
> instance MyClass Integer; instance SubClass Integer where bar = id
> foo 3
[3]
Upvotes: 5