Question

Is there a way to have default type instances defined in terms of each other? I'm trying to get something like this working:

{-# LANGUAGE DataKinds, KindSignatures #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE UndecidableInstances #-}
data Tag = A | B | C

class Foo (a :: *) where
    type Bar a (b :: Tag)

    type Bar a A = ()
    type Bar a B = Bar a A
    type Bar a C = Bar a A

instance Foo Int where
    type Bar Int A = Bool

test :: Bar Int B
test = True

but this doesn't work:

Couldn't match type `Bar Int 'B' with `Bool'
In the expression: True
In an equation for `test': test = True

Note that this doesn't work either:

test :: Bar Int B
test = ()
Was it helpful?

Solution

Yes, default type instances can be defined in terms of each other (as you can see from your own example):

instance Foo Int where
--    So the default recursive definition will be used instead
--    type Bar Int A = Bool

test :: Bar Int B
test = ()

However when you redefine associated type synonym in your instance definition for Int you replace entire default 3-line defintion of Bar (and not just the type Bar a A = ()) with one line type Bar Int A = Bool which means Bar Int B and Bar Int C are no longer defined.

So I guess one of the ways to use recursive defaults the way you intended is to redefine specific synonyms instead (though it is rather verbose):

class Foo (a :: *) where
    type Bar a (b :: Tag)
    type Bar a A = BarA a
    type Bar a B = BarB a

    type BarA a
    type BarA a = ()

    type BarB a
    type BarB a = Bar a A

-- This now works
instance Foo Int where
    type BarA Int = Bool

test :: Bar Int B
test = True

Which can fall back to defaults:

-- As well as this one
instance Foo Int where
--    type BarA Int = Bool

test :: Bar Int B
test = ()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top