Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Parametrised classes

I have a class as follows:

class Token a where
    symbol :: a -> String

I also want all instances of Token to have a function convert which returns a parametrised type. The conversion alone works fine:

class Token a b where
    convert :: a -> b

data Egal = One | Two

instance Token Egal Int where
    convert One = 111
    convert Two = 222

main = print $ show (convert One :: Int)

But when I try to use both symbol and convert I get errors about ambiguity. This is my code:

class Token a b where
    convert :: a -> b
    symbol :: a -> String

data Egal = One | Two

instance Token Egal Int where
    convert One = 111
    convert Two = 222
    symbol One = "one"
    symbol Two = "two"

main = print $ show (convert One :: Int)

What am I doing wrong?


EDIT: Reading my own question I started wondering: Should these be two distinct classes and my data Egal show instanciate both?

like image 749
Chirmol Studio Avatar asked Mar 21 '26 17:03

Chirmol Studio


1 Answers

As you have defined things here, you can have instances with the same a but conflicting bs. Like this:

instance Token Char Char where
    convert = id
    symbol c = [c]

instance Token Char Bool where
    convert = (>'m')
    symbol c = [c, c, c, c]

Now, should symbol 'x' be "x" or "xxxx"? Both are possible depending which of the above instances gets chosen; it is ambiguous which instance should be used for symbol, and therefore which answer you should get. There are various ways to fix this. One is to simply allow the ambiguity, and give yourself the ability to specify which instance to use at call sites. You can turn on the AllowAmbiguousTypes and TypeApplications extensions; then:

> symbol @_ @Char 'x' -- or, more explicitly, symbol @Char @Char 'x'
"x"
> symbol @_ @Bool 'x' -- explicitly, symbol @Char @Bool 'x'
"xxxx"

But in many cases, you really want the compiler to check that you haven't made multiple instances with conflicting as. Then you can use either the FunctionalDependencies extension:

class Token a b | a -> b where
    convert :: a -> b
    symbol :: a -> String

instance Token Char Char where {- ... -}

or the TypeFamilies extension:

class Token a where
    type Conversion a
    convert :: a -> Conversion a
    symbol :: a -> String

instance Token Char where
    type Conversion Char = Char
    {- ... -}

They have more or less the same effect in most cases: conflicting instances are flagged, and there is no ambiguity left.

like image 196
Daniel Wagner Avatar answered Mar 24 '26 19:03

Daniel Wagner



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!