10

For example, I have a type class :

class MyClass a b c where
    fun01 :: a -> b
    fun02 :: a -> c

    fun03 :: a -> b -> c -> ()
    fun04 :: a -> WhatEver

I'd like to provide a default implementation for my, let's call it BaseDataType which defines implementations of fun03 in terms of it self and fun01 and fun02. Then I'd have something like this :

class MyClass BaseDataType b c where
    fun03 = fun01 <$> fun02 ...
    fun04 = fun02 ...

And than to finalize my class instance and avoid all the boilerplate code for fun03 and fun04 I'd just provide fun01 and fun02 like this :

instance MyClass BaseDataType Int Char where
    fun01 = 1
    fun02 = 'C'

Is there possibly some language extension that allows this kind of behaviour? I couldn't find anything on this topic.

duplode
  • 33,731
  • 7
  • 79
  • 150
Reygoch
  • 1,204
  • 1
  • 11
  • 24

2 Answers2

11

There is no such extension, but you can achieve this functionality simply by splitting your class into two classes:

class MyClass1 a b c where
    fun03 :: a -> b -> c -> ()
    fun04 :: a -> WhatEver

class MyClass1 a b c => MyClass2 a b c where    
    fun01 :: a -> b
    fun02 :: a -> c

Then your instances will work the way you want:

-- NB: need the MyClass2 constraint if you use `fun01` or `fun02` in the definitions
-- This requires UndecidableInstances
instance MyClass2 BaseDataType b c => MyClass1 BaseDataType b c where
    fun03 = fun01 <$> fun02 ...
    fun04 = fun02 ...

instance MyClass2 BaseDataType Int Char where
    fun01 = 1
    fun02 = 'C'

Users of your class are not affected; they can continue to consume MyClass2 where they used MyClass before and get the exact same functionality.


Aside: the original definition of MyClass, and MyClass1 and MyClass2 don't even compile due to several ambiguous type errors (c is not mentioned in the type of fun01, etc.) - I assume this class was defined just for demonstration purposes and I haven't tried to fix this.

user2407038
  • 14,400
  • 3
  • 29
  • 42
5

You can use DefaultSignatures like this

class MyClass a b c where
    fun01 :: a -> b
    fun02 :: a -> c

    fun03 :: a -> b -> c -> ()
    default fun03 :: (a ~ BaseDataType) => a -> b -> c -> ()
    fun03 = fun01 <$> fun02 ...

    fun04 :: a -> WhatEver
    default fun04 :: (a ~ BaseDataType) => a -> WhatEver
    fun04 = fun02 ...
Alexey Vagarenko
  • 1,206
  • 8
  • 15
  • 2
    +1, but I don't reckon this is usuable for the OP because the default is _for all_ `MyClass` instances, not just for the `MyClass BaseDataType` specialisation. (In other cases, the default implementation will just give a obscure-ish type error to indicate that a custom instance must be replaced.) – leftaroundabout Dec 18 '17 at 23:23