Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 943
hugomg Avatar asked Oct 29 '12 05:10

hugomg


People also ask

What is a type synonym in Haskell?

From HaskellWiki. A type synonym is a new name for an existing type. Values of different synonyms of the same type are entirely compatible. In Haskell you can define a type synonym using type : type MyChar = Char.

What is an instance of a Haskell type class?

An instance of a class is an individual object which belongs to that class. In Haskell, the class system is (roughly speaking) a way to group similar types. (This is the reason we call them "type classes"). An instance of a class is an individual type which belongs to that class.

What is deriving show in Haskell?

Deriving means that your data type is automatically able to "derive" instances for certain type classes. In this case BaseballPlayer derives Show which means we can use any function that requires an instance of Show to work with BaseballPlayer .


2 Answers

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.

like image 70
hammar Avatar answered Oct 21 '22 01:10

hammar


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.

like image 26
Antal Spector-Zabusky Avatar answered Oct 20 '22 23:10

Antal Spector-Zabusky