Guarantying assignment to a function's return value in OCaml
Coming to OCaml from Lisp, I find myself very confused about when functions return and when they don't. I miss my magic Quote! Thankfully, most of the time, OCaml ap开发者_开发百科pears to automagicly know when I want a function evaluated and when I don't. However, I frequently find myself trying to assign the return value of a function in a let expression, like the following.
let start = Sys.time in
(*
* do something here
*)
;
let ending = Sys.time in
Printf.printf "did something in %f seconds\n" (ending -. start)
but then ocamlc complains
Error: This Expression has type unit -> float
but an expression was expected of type float
Telling me that start and end are bound to Sys.time
, not the return value of Sys.time
.
Is this behavior I'm trying to get not OCamly? Do I want to be doing things another way? Am I just missing something completely obvious?
A function is evaluated when you apply it to an argument. I.e. when you do f
, f
never gets evaluated. When you do f x
, f
always gets evaluated. There's nothing magical about it.
As you correctly pointed out, Sys.time
is a function (of type unit -> float
) and let start = Sys.time
just assigns that function to start
.
To get the behavior you want simply do let start = Sys.time ()
, which applies the function Sys.time
to the argument ()
(which is the only value of type unit
).
You cannot call a function just by writing its name. If you just write a function's name, you're returning the function itself, not its return value. That error is telling you that the function takes a unit
argument — i.e., you should write Sys.time ()
to actually apply the function and get the resulting float
value.
To help people used to Lisp, I would say that there are only two evaluation rules in OCAML:
- Delayed evaluation rule: A function value, such as
fun x -> body
, when not applied to any argument, will not be evaluated any further. (The evaluation of a function body is “delayed”.) Instead, the expression “body” is compiled into computer code. That computer code is the real “value” of the function expression, and the code will be run whenever the function is applied to an argument. - Eager evaluation rule: In evaluating
f x
, the argumentx
is evaluated first. (Functions are “eager to evaluate their arguments”.) Then the function expressionf
is evaluated, which generally yields a function value such asfun x -> body
. (Here,body
is not yet evaluated; only as much is evaluated that we get a function valuefun x -> body
. For example,f
could be a complicated expression that yields such a function value as a result.) Finally, the body of the resulting function is applied to the actually computed value of the argument (i.e. thebody
is evaluated withx
substituted by the computed value of the argument).
For this reason, you can implement "quote" in OCAML, if you want to delay the evaluation of some expression, only by putting it inside the body of a function expression. For example, if you have previously computed f
by let f = fun x->x+1
and now you want to delay the evaluation of f 3
, you can put this f 3
into the body of a function:
let delay_f () = f 3;;
Now you will get 4 only when you evaluate delay_f ()
. You can pass the value delay_f
to another function, and f 3
will stay unevaluated until somebody evaluates delay_f ()
.
精彩评论