In Haskell, is it possible to provide default implementation for partially applied multi-param type class?
Asked Answered
B

2

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.

Boland answered 18/12, 2017 at 16:1 Comment(0)
M
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.

Microbiology answered 18/12, 2017 at 16:15 Comment(0)
S
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 ...
Stefa answered 18/12, 2017 at 23:17 Comment(1)
+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.)Passenger

© 2022 - 2024 — McMap. All rights reserved.