[Haskell-cafe] lifting restrictions on defining instances

wren ng thornton wren at freegeek.org
Tue Jul 28 04:58:16 EDT 2009

Tillmann Rendel wrote:
> wren ng thornton wrote:
>> [1] In System F the capital-lambda binder is used for the term-level 
>> abstraction of passing type representations. So for example we have,
>>     id :: forall a. a -> a
>>     id = /\a. \(x::a). x
>> Thus, the forall keyword is serving as the type-level abstraction. 
>> Perhaps this is suboptimal syntax, but it is the standard. We could, 
>> of course, have both a term-level /\ and a type-level /\ where the 
>> latter is the type of the former (since the namespaces are separate) 
>> though that's also dubious. Capital-pi is the canonical type-level 
>> abstraction, though that evokes the idea of dependent types which are 
>> much more complex.
> What do you mean by "type-level abstraction" here?

I mean an abstraction, as in a lambda-abstraction (aka a 
lambda-expression), at the type level.

> In a language with type functions and polymorphism, we need three 
> different lambda binders:
>   (1) abstraction over terms in terms (to construct functions)
>   (2) abstraction over types in terms (to construct polymorphic values)
>   (3) abstraction over types in types (to construct type functions)
> I think only (2) should be written as upper-case lambdas, while (1) and 
> (3) should both be written as lower-case lambdas. Since (1) and (3) 
> belong to different syntactic categories, they can not be confused, and 
> we can reuse the lower-case lambda at the type-level.

I'm sure that's fine. I was merely pointing out precedent.

The syntax of #3 could also be conflated with the syntax of #2, for the 
same reason: they are in different syntactic categories. I pointed this 
out because the capital-lambda was the syntax others in the thread were 
using. Also, it makes sense to me to have #2 and #3 ("abstraction over 
types in _") paired together, rather than #1 and #3 ("abstraction over X 
in X"). Pairing #2/#3 also gives term/type symmetry as we have for other 
built-ins like [], (,), and -> (though the symmetry is imperfect for ->).

> Furthermore, we need three function types / kinds to describe the three 
> lambdas one level higher:
>   (4) the type of functions
>   (5) the type of polymorphic values
>   (6) the kind of type functions
> In ghc, we already have "forall" for (5), and arrows for (4) and (6).
> I would say that (3) is the "type-level abstraction", not (5).

I'm not sure I follow what you mean. Given the relationship between /\ 
and forall as demonstrated in the definition of id above, I don't see 
such a difference between #3 and #5. That is, given the need for #2 the 
need for #5 follows; from there #3 follows by extending the wording of 
#5. (Though #6 is desirable from a theoretical perspective I'm not sure 
whether the language needs to be able to express it. There's much else 
at the kind-layer we cannot express.)

In other words, just because forall is the type of /\ doesn't mean that 
that's all it is. All of these are effectively identical:

     -- Just a type-level lambda
     five :: (forall a. a) Int
     five = 5

     -- Making the term-level match the type exactly
     five :: (forall a. a) Int
     five = (/\a. 5::a) @Int

     -- Hiding a CAF behind a constant type
     -- (somewhat like how numeric constants really work)
     five :: Int
     five = (/\a. 5::a) @Int

     -- Boring
     five :: Int
     five = 5

Live well,

More information about the Haskell-Cafe mailing list