What is the effect of type synonyms on instances of type classes? What does the TypeSynonymInstances pragma in GHC do?
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 pa开发者_JAVA技巧rameter 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 constructorString
is a data constructor AND type synonym
Now I can't answer these questions:
- 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)?
- Why did the designers of the language, not want this syntax (I'm asking for reasoning not extensive theory or unicode math symbols).
- 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!
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
ornewtype
, nottype
. This forbidsString
, 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
andf Int
, but notMaybe 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).
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.
Haskell 98
- an instance head must have the form C (T u1 ... uk), where T is a type constructor defined by a data or newtype declaration (see TypeSynonymInstances) and the ui are distinct type variables, and
- each assertion in the context must have the form C' v, where v is one of the ui.
So it is valid to use instance ClassName TypeConstructor where
and TypeConstructor MUST be such as Int
, Double
or [a]
, make sure that only can a type constructor be involved!!
BTW, []
is type constructor, so [TypeConstructor]
cannot be used but [NewType]
and [TypeVariable]
are allowed.
This is a restriction is Haskell, and we can avoid it by enabling FlexibleInstances.
Int and String are types, not data construtors. String happens to be an alias for [Char] which can also be written List Char. A data constructor is something like Just, so Just 3 is a value of type Maybe Int. Type synonym instances are explained here:
http://hackage.haskell.org/trac/haskell-prime/wiki/TypeSynonymInstances
精彩评论