hugomg
hugomg

Reputation: 69934

Why does Haskell not allow Type Synonyms when declaring class instances?

While I know that there is a TypeSynonymInstances extension in GHC, I have no idea how "dangerous" it is and I wonder if this restriction is arbitrary, kind of like the Monomorphism Restriction, or if there are deeper reasons for it.

Upvotes: 36

Views: 8420

Answers (3)

augustss
augustss

Reputation: 23014

It used to be allowed, but in attempt to make Haskell less full of surprises to beginners it was banned.

Upvotes: 6

hammar
hammar

Reputation: 139850

TypeSynonymInstances is perfectly safe. Since anything potentially fancy like partially applied type synonyms is disallowed, it has exactly the same effect as typing out the the right hand side of the type synonym in the instance head, i.e.

type Foo a = ([a], [a])
instance Bar (Foo a)

is the same as

instance Bar ([a], [a])

However, note that both instances require FlexibleInstances since they contain nested type constructors as well as repeated type variables. In general, this will often be the case after expanding type synonyms.

I think may be the reason why they're disallowed by default.

However, FlexibleInstances is also a perfectly safe extension. The worst it can do is cause a compile-time error if you try to define overlapping instances such as

instance Xyzzy String
instance Xyzzy [a]

As for why FlexibleInstances is not available by default, I can only guess that it's to simplify the language. The standard rules for instance heads ensures that the only way instance definitions can overlap is if the type constructors in the instance heads are identical, while with FlexibleInstances checking for overlaps is slightly more difficult.

Upvotes: 31

Antal Spector-Zabusky
Antal Spector-Zabusky

Reputation: 36622

As I understand it, it's sort of like the monomorphism restriction—there's nothing wrong about getting rid of it, but it opens you up to behavior you might not expect. Just like the monomorphism restriction doesn't hurt anything—all the types are still valid—this also ought to be totally safe: there are restrictions on type synonyms anyway which prevent them from doing anything fancier than simple name shortening (e.g., you can never partially apply them, so we don't get type-level lambdas), and so you can always replace them with the right-hand side of their definition. Thus, since the right-hand sides of those definitions can be checked as instance heads (or contain further type synonyms to expand out), nothing unsafe should be going on.

On the other hand, just as disabling the monomorphism restriction opens you up to potentially odd performance characteristics, enabling type synonym instances opens you up to potentially odd type class errors. So let's enable -XTypeSynonymInstances and try writing an instance with a type synonym:

Prelude> :set -XTypeSynonymInstances
Prelude> instance Num String where (+) = (++)

<interactive>:3:10:
    Illegal instance declaration for `Num String'
      (All instance types must be of the form (T a1 ... an)
       where a1 ... an are *distinct type variables*,
       and each type variable appears at most once in the instance head.
       Use -XFlexibleInstances if you want to disable this.)
    In the instance declaration for `Num String'

String looks like a plain old type, so this might be surprising at first; but it's really [Char], and so this instance is invalid according to Haskell 2010's strict rules. If we relax those rules by turning turn on -XFlexibleInstances (which, incidentally, implies -XTypeSynonymInstances), this example works now:

Prelude> :set -XFlexibleInstances
Prelude> instance Num String where (+) = (++)
... errors about undefined methods ...
Prelude> "a" + "b"
"ab"

But things gets ugly fast:

Prelude> instance Eq String where
Prelude> "a" == "b"

<interactive>:8:5:
    Overlapping instances for Eq [Char]
      arising from a use of `=='
    Matching instances:
      instance Eq a => Eq [a] -- Defined in `GHC.Classes'
      instance Eq String -- Defined at <interactive>:7:10
    In the expression: "a" == "b"
    In an equation for `it': it = "a" == "b"

Again, even though String looks like a distinct type, we already have an instance for [a], and so this overlaps with it. (And in fact, this is probably part of why -XFlexibleInstances isn't on by default.) And turning on -XOverlappingInstances is a much dodgier idea than turning on -XFlexibleInstances.

Upvotes: 18

Related Questions