Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the effect of type synonyms on instances of type classes? What does the TypeSynonymInstances pragma in GHC do?

Tags:

I'm reading Real World Haskell Pg 151, and I've stared at the following passage for over an hour:

Recall that String is a synonym for [Char], which in turn is the type [a] where Char is substituted for the type parameter a. According to Haskell 98's rules, we are not allowed to supply a type in place of a type parameter when we write an instance. In other words, it would be legal for us to write an instance for [a], but not for [Char]. 16 comments 5335

It simply isn't sinking in. Staring at the the (free not pirated) copy of RWH chapter 6 I see a lot of other people are really suffering with this. I still don't understand it from the comments...

Firstly, everything about this confuses me, so please if you feel you can explain anything about this passage, or TypeSynonymInstances please do.

Here is my problem:

  • Int is a data constructor
  • String is a data constructor AND type synonym

Now I can't answer these questions:

  1. Why would a type synonym preclude the making the type a member of a type class (I'm looking for some reason which probably relates to compilation or implimentation of a type synonym)?
  2. Why did the designers of the language, not want this syntax (I'm asking for reasoning not extensive theory or unicode math symbols).
  3. I see this line "the type [a] where Char is substituted for the type parameter a", and I want to know why I can't substitute it for this "the type a where Int is substituted for the type parameter a".

Thanks!

like image 929
NO WAR WITH RUSSIA Avatar asked Jan 24 '10 02:01

NO WAR WITH RUSSIA


2 Answers

I think part of the issue is that two, largely unrelated, restrictions are in play:

  • No type synonym instances means that instances can only be things declared with data or newtype, not type. This forbids String, but not [Char].
  • No flexible instances means that instances can only mention one type that isn't a variable, and only that type can be used as a type constructor. This forbids Maybe Int and f Int, but not Maybe a.

Here's what GHCi says about Int, Char, and String:

data Char = GHC.Types.C# GHC.Prim.Char#
data Int = GHC.Types.I# GHC.Prim.Int#
type String = [Char]

Int and Char are both simple types without type variable parameters; there's no type constructor involved, so you can make instances with them pretty much freely.

String, however, fails on both counts. It's a type synonym, which isn't allowed, and it's also a type constructor applied to a non-variable, namely the list type constructor applied to Char.

For comparison, note that [a], Maybe a, and Either a b are all valid in instances, but [Int], Maybe [a], and Either String a are forbidden; hopefully you can now see why.

As for your direct questions, I don't know what the original motivations were for designing the language that way, and I'm in no way qualified to make authoritative statements about "best practices", but for my own personal coding I don't really hesitate to use these pragmas:

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE EmptyDataDecls #-}
{-# LANGUAGE TypeSynonymInstances #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE FlexibleContexts #-}

You could always go look at packages that use pragmas. Flexible instances, it seems, do get a fair amount of use, and from "respectable" packages (there's a couple hits in the source for Parsec, for instance).

like image 70
C. A. McCann Avatar answered Oct 07 '22 19:10

C. A. McCann


Actually, neither Int nor String are data constructors. That is, you can't use them to create a value of

> (Int 42, String "bob")
<interactive>:1:1: Not in scope: data constructor `Int'
<interactive>:1:9: Not in scope: data constructor `String'

Int names a new, distinct, algebraic data type. String is a "type-synonym", or alias, for the already existing type: [Char]. The problem is that Haskell 98 says you can't use a type synonym in an instance declaration.

I can't say why authors of the Haskell 98 report choose to restrict type synonyms in this case. There are quite a number of restrictions on them. For example, they cannot be partially applied (if they take type arguments). I think a clue comes at the end of §4.2.2:

Type synonyms are a convenient, but strictly syntactic, mechanism to make type signatures more readable. A synonym and its definition are completely interchangeable, except in the instance type of an instance declaration (Section 4.3.2).

Presumably, there was an approach to program compilation, for which this syntactic interchangeability would have caused problems for instances. Perhaps it has to do with notable aspect of instances that they leak out of packages...

As to your last question, I believe that explanation is conflating two things: 1) String is a type synonym for [Char], which is in turn a specialization of the more general type [a] and 2) that even without the synonym, [Char] cannot be used in the head of an instance.

This second problem has nothing to do with type synonyms, but that instance heads must have all the type parameters to the type constructor be variables, not concrete types. That is, you can't define separate instances for [Int] and [Char] for some class, you can only defined an instances [a]. (Remember, that despite the convenient syntax, [] is a type constructor, and the thing inside is the type parameter.)

Again, I don't know why the report restricts these, but I suspect it also has to do with compilation strategy. Since GHC's compilation strategy for instances can handle this, you can relax this constraint in GHC via -XFlexibleInstances.

Finally, I've seen both extensions turned on in quite a lot of code, but perhaps someone with more Haskell experience can weigh in as to if they are "best practices" or no.

like image 31
MtnViewMark Avatar answered Oct 07 '22 18:10

MtnViewMark