问题
I can't for the life of me figure out why the following SML function is throwing a Warning in my homework problem:
fun my_func f ls =
case ls of
[] => raise MyException
| head :: rest => case f head of
SOME v => v
| NONE => my_func f rest
fun f a = if isSome a then a else NONE;
Whenever I call my_func with the following test functions:
my_func f [NONE, NONE];
my_func f [];
I always get the warning:
Warning: type vars not generalized because of value restriction are instantiated to dummy types (X1,X2,...)
Whenever I pass in an options list containing at least one SOME value, this Warning is not thrown. I know it must be something to do with the fact that I am using polymorphism in my function currying, but I've been completely stuck as to how to get rid of these warnings.
Please help if you have any ideas - thank you in advance!
回答1:
The value restriction referenced in the warning is one of the trickier things to understand in SML, however I will do my best to explain why it comes up in this case and try to point you towards a few resources to learn more.
As you know, SML will use type inference to deduce most of the types in your programs. In this program, the type of my_func
will be inferred to be ('a -> 'b option) -> 'a list -> 'b
. As you noted, it's a polymorphic type. When you call my_func
like this
myfunc f [NONE, SOME 1, NONE];
... the type variables 'a
and 'b
are instantiated to int option
and int
.
However when you call it without a value such as SOME 1
above
myfunc f [NONE, NONE];
What do you think the type variables should be instantiated to? The types should be polymorphic -- something like 't option
and 't
for all types 't
. However, there is a limitation which prevents values like this to take on polymorphic types.
SML defines some expressions as non-expansive values and only these values may take on polymorphic types. They are:
- literals (constants)
- variables
- function expressions
- constructors (except for
ref
) applied to non-expansive values - a non-expansive values with a type annotation
- tuples where each field is a non-expansive value
- records where each field is a non-expansive value
- lists where each field is a non-expansive value
All other expressions, notably function calls (which is what the call to my_func
is) cannot be polymorphic. Neither can references. You might be curious to see that the following does not raise a warning:
fn () => my_func f [NONE, NONE];
Instead, the type inferred is unit -> 'a
. If you were to call this function however, you would get the warning again.
My understanding of the reason for this restriction is a little weak, but I believe that the underlying root issue is mutable references. Here's an example I've taken from the MLton site linked below:
val r: 'a option ref = ref NONE
val r1: string option ref = r
val r2: int option ref = r
val () = r1 := SOME "foo"
val v: int = valOf (!r2)
This program does not typecheck under SML, due to the value restriction. Were it not for the value restriction, this program would have a type error at runtime.
As I said, my understanding is shaky. However I hope I've shed a little light on the issue you've run into, although I believe that in your case you could safely ignore the warning. Here are some references should you decide you'd like to dig deeper:
http://users.cis.fiu.edu/~smithg/cop4555/valrestr.html
http://mlton.org/ValueRestriction
(BTW the MLton site is solid gold. There's so much hidden away here, so if you're trying to understand something weird about SML, I highly recommend searching here because you'll likely turn up a lot more than you initially wanted)
Since it seems like you're actually using SML/NJ, this is a pretty handy guide to the error messages and warnings that it will give you at compile time:
http://flint.cs.yale.edu/cs421/smlnj/doc/errors.html
来源:https://stackoverflow.com/questions/14668433/sml-warning-type-vars-not-generalized-when-using-empty-lists-or-none-option