This is more of a standard ML question, but I figure folks here will know. I've always found the situations in which poly/ML allows polymorphic values (versus forcing a monotype) a bit puzzling. Take this bit of code, for instance:
datatype 'a box = Empty | Box of 'a datatype 'a wrap = Wrap of 'a val box = Box val wrap = Wrap
val empty = Empty
(* first situation *) val x = Wrap empty (* polymorphic value : 'a box wrap *) val y = wrap empty (* monotype : _a box wrap *)
(* second situation *) fun wrapbox1 x = wrap (box x) (* : 'a -> 'a box wrap *) val wrapbox2 = wrap o box (* : _a -> _a box wrap *)
In the first situation, its clear that SML treats a type constructor differently than it's associated function, which makes sense. I've always treated the "val foo = Foo" syntax as syntactic sugar for "fun foo x = Foo x". Is this right, or only approximately right?
In the second situation, it seems like the expression "val wrapbox2 = wrap o box" is forcing "wrap o box" to be executed "too soon", so the compiler has no choice but to give it a monotype. For instance, changing the expression to "fn () => (wrap o box)" preserves the polymorphism.
So, my main question: Is there a reasonable/memorable set of rules for determining when an expression will produce a polymorphic value as opposed to a monotype? Is this written down somewhere (e.g. in the SML 97 spec/commentary)?
A second, more specific question: Is there a way for values resulting from a function call to maintain their polymorphism? That is, can I produce a polymorphic empty value of type " 'a box wrap " using just "wrap : 'a -> 'a wrap" and "empty : 'a box"?
Where everything is at the top-level, this doesn't seem necessary. However, with a bunch of structures and abstract types around, I've run into this dead end before. Is it possible to get something like "val empty = Foo.mk (Bar.empty, Baz.empty)", with "Bar.empty : 'a bar", and "Baz.empty : 'a baz" and "val empty : ('a bar, 'b baz) foo"?
The only solutions I've found are ditching the nice abstract type Foo.T by moving the datatype def to the signature (boo!) or replacing "val empty" with "fun empty () = ..." and needing to write "(empty ())" everywhere (double boo!). Does anyone know a nice way around this?
Aleks,
On Mon, 2013-04-15 at 19:15 +0100, Aleks Kissinger wrote:
This is more of a standard ML question, but I figure folks here will know. I've always found the situations in which poly/ML allows polymorphic values (versus forcing a monotype) a bit puzzling. [...]
Perhaps others will answer in more detail, but until then, you could try searching for "SML value restriction".
Best, Tjark
Ah, I see. Had a look at this note: http://users.cis.fiu.edu/~smithg/cop4555/valrestr.html
So, the point is the if I'm going to write somethink like "val x = ...", then (...) should not do any computation, so it can only consist of constants, constructors, identifiers, or lamba-expressions. As the author says, eta expansion fixes the second situation I described:
val f = g o h ===> val f = fn x => (g o h) x
However, this only works when the desired value has function type. For the first situation (replacing a constructor with a function), this could just be one of those "SML can't do that" situations. To use the eta-expansion trick, there would need to be something like parameter-less delayed evaluation (as in e.g. scala). Another thing I tried was to have a constructor "masquerade" as a function:
signature FOO = sig type 'a T val mk : 'a * int -> 'a T end
structure Foo :> FOO = struct datatype 'a T = mk of ('a * int) end
but this doesn't work, because Foo.mk (to the outside world) is not a constructor, but rather its associated function. Also, weird constructor names are not so good for nice code and error messages.
a
On 15 April 2013 20:57, Tjark Weber tjark.weber@gmx.de wrote:
Aleks,
On Mon, 2013-04-15 at 19:15 +0100, Aleks Kissinger wrote:
This is more of a standard ML question, but I figure folks here will know. I've always found the situations in which poly/ML allows polymorphic values (versus forcing a monotype) a bit puzzling. [...]
Perhaps others will answer in more detail, but until then, you could try searching for "SML value restriction".
Best, Tjark