4
votes

Consider the following definitions:

class Foo a where
    foo :: a -> Int

class Bar a where
    bar :: a -> [Int]

Now, how do I say "every Foo is also a Bar, with bar defined by default as bar x = [foo x]" in Haskell?

(Whatever I try, the compiler gives me "Illegal instance declaration" or "Constraint is no smaller than the instance head")

Btw, I can define my Foo and Bar classes in some other way, if this would help.

3
This is related to (duplicate of?) stackoverflow.com/questions/3213490/…John L

3 Answers

11
votes
class Foo a where
    foo :: a -> Int

-- 'a' belongs to 'Bar' only if it belongs to 'Foo' also
class Foo a => Bar a where
    bar :: a -> [Int]
    bar x = [foo x] -- yes, you can specify default implementation

instance Foo Char where
    foo _ = 0

-- instance with default 'bar' implementation
instance Bar Char
4
votes

As the automatic definition of a Bar instance through a Foo instance can lead to undecidable cases for the compiler - i.e. one explicit instance and one through Foo conflicting with each other - , we need some special options to allow the desired behaviour. The rest through is quite straigtforward.

{-# LANGUAGE FlexibleInstances, UndecidableInstances #-}

class Foo a where
    foo :: a -> Int

class Bar a where
    bar :: a -> [Int]

instance (Foo a) => Bar a where
    bar x = [foo x]
4
votes

Generally speaking you don't model things with type classes this way[*] - i.e. an instance of a type class should always be some concrete type, though that type itself can be parameteric - e.g. the Show instance for pair has this signature:

instance (Show a, Show b) => Show (a,b) where

Some of the approaches to "Generics" allow you to model a general base case and then have type specific exceptional cases. SYB3 allowed this - perhaps unfortunately SYB3 isn't the common practice Generics library it is Data.Data / Data.Generics which I think is SYB1.

[*] In the wild the story is a little more complicated - as Dario says UndecidableInstances can enable it.