In Chameleon the scope of type variables introduced in a type annotation is the entire body of that declaration. Thus, we assume that all variables occuring in a type annotation are implicitely quantified unless the variable has been introduced by an annotation in the outer scope.
Consider the program.
prefix :: b->[[b]]->[[b]] prefix x yss = let xcons :: c -> [b]->[b] xcons v ys = x:ys in map (xcons undefined) yssQuantifiers are left implicit. Note that function prefix has type forall b.b->[[b]]->[[b]] whereas xcons has type forall c.c->[b]->[b]. Lexically scoped annotations in Chameleon differ to the ones found in GHC. The proposal by Peyton-Jones and Shields makes it necessary to introduce scoped type variables explicitely in pattern bindings. Here is the Haskell version of the previous example.
prefix :: a->[[a]]->[[a]] prefix (x::b) yss = let xcons :: c -> [b]->[b] xcons v ys = x:ys in map (xcons undefined) yssIn our opinion, scoped variables introduced in pattern bindings are redundant. In particular, this becomes apparant in case we switch to an explicitly typed System F style translation where universal quantifiers are represented by typed lambdas. The above example can be translated as follows.
prefix /\ b. \ x::b -> \yss:b -> let xcons = /\ c. \v::c -> ys::b -> x:ys in ....Currently, the proposal by Peyton-Jones and Shields does not allow references to scoped constraints appearing only in the type component which is naturally supported in our approach. Consider the program:
-- introduce eval as an overloaded identifier overload eval f :: (Eval (a->(b,c))) => a->b f x = fst ((eval x) :: (b,c))The lexically scoped variable c refers to the ``global'' constraint Eval (a->(b,c)). Note that we automatically connect lambda-bound variables to type annotations. For example, variable x has type a. Hence, we avoid the need for pattern bindings. Here is the previous example with pattern bindings.
f :: (Eval (a->(b,c))) => a->b f (x::a) = fst ((eval x) :: (b,c))Note that in Chameleon we do not support pattern bindings because such bindings can be derived from the annotation.
In Chameleon, we also support a form of ``existential'' type annotation. Consider the following program.
f ::: a->a f True = FalseThe annotation f ::: a->a states that f has type a->a for some a. We can arbitrarily mix both forms of annotations. Consider
f ::: a->a f x = let g :: a->a g y = undefined in g xConsider the following program. Our intention is to enforce that x and y are of the same type.
f x y = (x:::a, y:::a)However, the two occurences of variable a refer to different scopes. The following code does the job.
f x y = let z ::: a->c z _ = (x:::a, y:::a) in z undefinedNote that this trick can be used to introduce a ``shared'' scoped type variable.
Chameleon programs are translated to plain Haskell (i.e. Chameleon overloaded identifiers are resolved, that's it). Currently, we employ the following strategy.