Jason Hu
Jason Hu

Reputation: 6333

How to define default implementation in subclass definition in Haskell?

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

Answers (2)

Kwarrtz
Kwarrtz

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

Daniel Wagner
Daniel Wagner

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

Related Questions