A case where (1) fails but (2) does not is trivial; because type synonyms (type ExampleOfATypeSynonym = ...
) are not allowed in instance declarations, but they are allowed in constraints, any situation where you have only one instance like this:
-- (1)
class Foo a
type Bla = ()
instance Foo Bla
... can be transformed into:
-- (2)
class Foo a
type Bla = ()
instance (a ~ Bla) => Foo a
The only reason why (1) fails is because type synonyms are not allowed in instance declarations, and that's because type synonyms are like type functions: they provide a one-way mapping from a type name to a type name, so if you have a type B = A
and an instance Foo B
, it is non-obvious that an instance of Foo A
is created instead. The rule exists so that you have to write instance Foo A
instead to make it clear that that is the type that actually gets the instance.
The use of type families is irrelevant in this context, because the problem is rather that you're using a type synonym, the NameRecord
type. You have to also keep in mind that if the type synonym is removed and replaced by FieldOf Name
directly, the compile will still fail; that is because a "type family" is just an enhanced version of type synonyms, so FieldOf Name
is also a "type synonym" for Name :> Text
in this context. You have to use a data family and a data instance instead to get a "bidirectional" association.
More information about data families can be found in the GHC documentation.
I think you mean "... where (2) fails but (1) does not..."
Let's imagine that we have a type class like so:
class Foo a where
foo :: a
Now, you can write instances like so:
instance Foo Int where
foo = 0
instance Foo Float where
foo = 0
main :: IO ()
main = print (foo :: Float)
This works as one would expect. However, if you transform the code into this:
{-# LANGUAGE FlexibleInstances, TypeFamilies #-}
class Foo a where
foo :: a
instance (a ~ Int) => Foo a where
foo = 0
instance (a ~ Float) => Foo a where
foo = 0
main :: IO ()
main = print (foo :: Float)
It doesn't compile; it displays the error:
test.hs:5:10:
Duplicate instance declarations:
instance a ~ Int => Foo a -- Defined at test.hs:5:10-27
instance a ~ Float => Foo a -- Defined at test.hs:8:10-29
So, this is the example you hopefully were looking for. Now, this only happens if there is more than one instance of Foo
that uses this trick. Why is that?
When GHC resolves type classes, it only looks at the instance declaration head; i.e. it ignores everything before the =>
. When it has chosen an instance, it "commits" to it, and checks the constraints before the =>
to see whether they are true. So, at first it sees two instances:
instance Foo a where ...
instance Foo a where ...
It is clearly impossible to decide which instance to use based on this information alone.