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:
Thanks!
I think part of the issue is that two, largely unrelated, restrictions are in play:
data
or newtype
, not type
. This forbids String
, but not [Char]
.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).
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With