简体   繁体   中英

Why can't I provide a type within a typeclass instance?

This code works fine as expected:

data Heh a = Heh a
instance (Eq a) => Eq (Heh a) where
    (Heh a1) == (Heh a2) = a1 == a2

Yet this gives an error:

data Heh a = Heh a
instance (Eq a) => Eq (Heh a) where
    (Heh a1) == (Heh a2) = (a1 :: a) == a2
    -- Error: Couldn't match expected type ‘a1’ with actual type ‘a’
    --        ...

The only change is adding the :: a .

But why would this fail? Isn't a1 indeed a ? What else could it possibly be?

How does this relate to forall ? I understand Haskell has a forall keyword to deal with this somehow. (By the way, if that won't be possible until GHC 8.0, feel free to let me know how to do it in the upcoming GHC 8.0 then.)

the type checker does not know that the a in the last line is the same as the a given in the one above it, inside the where clause you have a new scope!

If you want to turn that off (getting new scopes) you need to enable ScopedTypeVariables which you can do by adding

{-# LANGUAGE ScopedTypeVariables #-}

to the top of your source file

or type :set -XScoped… inside ghci.

or call ghci with that ghci -XScoped… myfile.hs

You're correct that the resolution to this has to do with the forall keyword. The reason requires a bit of explanation, though. If you don't care for quite that much explanation, go ahead and skip to the end. If you still want more, see the Syntax Extensions and Other Type System Extensions sections of the GHC Manual.

Type declarations in Haskell that are parameterized by a type variable, such as data Heh a = Heh a , are essentially creating a type-level function (a type constructor) that takes a type as input and returns a new type. The "kind" of Heh (a kind is how we classify types, like types are how we classify values) is * -> * while Int is of kind * and Heh Int (the Heh type constructor applied to the type Int ) is of kind * as well. So a * -> * applied to a * gives another * , which is the kind of normal types.

Variables, whether they be at the value level or the type level, must be bound by some binding form , which defines the scope (region of the program text where the binding of name to specific variable is in effect) of the binding. At the value level, we're used to seeing these binders all over the place: some examples are the argument bindings in function definition patterns, the bindings in let expressions, and the bindings in lambda expressions (eg \\x -> x binds the name x within the body of the expression).

The confusion begins with type-level variables; they appear to have no binder at all, they're just there in the middle of type declarations. This is largely because Haskell's type system descends from the Hindley-Milner type system, which didn't originally have type annotations at all and used a somewhat different way of thinking about expressions that could take on multiple types. As notions of polymorphic types became more formalized, "polytyped" values from Hindley-Milner became "polymorphic" and the syntactically-typed version of the theory included type-level binders and corresponding value-level binders that would take a type and return an expression with the type substituted in the body of the expression for the type variable. Because these binders are resolved completely at compile time, they were left out of both the type- and value-level syntax. Since in the original system the binders can only ever occur in one position in a type declaration, there's no ambiguity there.

If you didn't follow all that, don't worry about it; the gist is that things worked out the way they did for historical reasons as the underlying concepts and the way they were implemented in typed functional languages evolved.

Anyway, the forall is the type-level variable binder, sort of like a lambda expression is a value-level variable binder. Without it, it is assumed that there's an implicit forall at the beginning of each type-level expression that binds all the free type variables. Explicitly binding the type variables makes it clearer that they're "universally-quantified" (see an intro to first-order logic if you're not familiar with the concept of universal quantification) and also opens the possibility of making "existentially-quantified" and higher-ranked polymorphic type variables (with the RankNTypes extension) as well. This may seem odd, since existential quantification is usually expressed via an "exists" binder, but if you put a forall a inside a data type declaration that doesn't bring the a into scope itself (eg data Foo b = forall a. Foo (a, a -> b) ) you get the same effect as an existential quantification on a (assuming you enable the ExistentialQuantification extension, of course).

But what you're really concerned about is how to increase the scope of your type variable quantification to the entire instance declaration; this is what the ScopedTypeVariables extension is for. As I said before, the Hindley-Milner type system originally didn't use the notion of bound and free type variables (there were type schemes (or polytypes) and monotypes, which played roles in a logical resolution algorithm) or even type annotations. When type annotations were first added in, they stood for "unknown monotypes" and referred to just the particular expression being annotated rather than being a scoped binding construct like a forall or lambda. Therefore, they were subject to renaming if the same name happened to show up more than once in a type environment. By turning on ScopedTypeVariables , any type variable expressed in the scope of an explicit forall binding that binds the same name is no longer an unknown monotype variable (ie "rigid type variable" from your error message) but instead a reference to the bound type variable of the same name in the nearest enclosing type binder for that name.

Anyway, here's the end result required to resolve your conundrum:

{-# LANGUAGE ScopedTypeVariables #-}

data Heh a = Heh a
instance forall a. (Eq a) => Eq (Heh a) where
   (Heh a1) == (Heh a2) = (a1 :: a) == a2

The scope of the forall in the instance declaration extends throughout the instance declaration, and since ScopedTypeVariables is active, the reference to a in the type annotation on a1 refers to the same a in the forall binding.

If you look at the documentation, you'll find there are a few other syntactic constructs that bind type variables as well. In this case, forall is not actually necessary because the class and instance variables in the head of class and instance declarations automatically bind the type variables in the same manner as forall does when the extension is active. But if you have a standalone function with a type declaration that needs to be scoped across the whole body of the function, you'll need to know how to use forall to get the type variable scoping you want.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM