Reputation: 13672
I recently noticed that a type variable is allowed before a function name in a function declaration. But I cannot see how it is used. Here are some examples using it:
Poly/ML 5.5.2 Release
> fun 'a print a = PolyML.print (a);
val print = fn: 'a -> 'a
> print "foo";
?
val it = "foo": string
> pint string "foo";
Error-Value or constructor (string) has not been declared
Found near print string "foo"
Static Errors
> string print "foo";
Error-Value or constructor (string) has not been declared
Found near string print "foo"
Static Errors
> val f : string -> string = print;
val f = fn: string -> string
> f "foo";
?
val it = "foo": string
So I have a few questions based on this. First, what is a good example of a use case for the type variable before the function name (as opposed to the more common type variable in an argument or return type signature). Also, is there a way to indicate that I want to specialize on the type like how I can with a type?:
> type 'a t = 'a list;
eqtype 'a t
> type f = string t;
type f = string t
I did declare a specialization by creating a new variable, val f
, with an explicit type signature, but I don't think this is the same thing. For instance, coming from the type example above I'd expect to be able to do this:
> val s = string print;
Error-Value or constructor (string) has not been declared Found near string print
Static Errors
But that fails.
Finally, why did the type variable hide the type of the argument inside the function? I am only guessing this happens at all because the PolyML.print function prints a question mark (indicating it doesn't know the type) rather than the actual value. Even when I declared the new function f
that clearly constrained the type, it still didn't know the type of the variables being passed. (Though I'm pretty sure this particular part has nothing to do with the initial type variable on the function but rather the (implicit) type variable on the argument a
.)
Upvotes: 3
Views: 232
Reputation: 526
The idea behind having type variables immediately after a fun
is that it scopes subsequent uses of the type variable. Consider the differences between
> fun f x =
# let
# fun I (y:'a): 'a = y
# in
# I I
# end;
val f = fn: 'a -> 'b -> 'b
and
> fun 'a f x =
# let
# fun I (y:'a): 'a = y
# in
# I I
# end;
Type error in function application.
Function: I : 'a -> 'a
Argument: I : 'a -> 'a
Reason:
Can't unify 'a to 'a -> 'a (Cannot unify with explicit type variable)
The first type-checks because the general use of I
can be specialised to different types. The second does not because by scoping the type variable at the fun you have said that I
should not be generalisable at that level. You have said that you want all occurrences of 'a
within f
to be the same.
ML has separate name spaces for values and types, as well as for different kinds of modules. string
is defined as a type but if you use this identifier in a context where a value is expected it will be undefined. You need to add a type constraint e.g. : string -> string
Finally, be careful about using PolyML.print
as an example. It's not an ordinary ML polymorphic function, rather an infinitely overloaded function that prints its argument depending on type information at compile-time. It's a Poly/ML extension, which is why it is in the PolyML structure, although print
was present in an early draft of what became Standard ML.
Upvotes: 5