New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Pushing the evaluation of default expression for optional arguments is not specified #5975
Comments
Comment author: @garrigue Indeed, the documentation is lacking here. Ideally indeed we would like to apply this transformation only when the expression is pure, but unfortunately there is no way to detect this in a meaningful way (we do not want any function application to disable this optimization). I will fix the documentation to say that the evaluation of default expressions can be delayed arbitrarily until the real body of the function is reached, but the order of default expressions is preserved. Also you are right that this optimization does not apply to object constructors, since their compilation path is completely different. It is not a good idea to use many optional arguments in an object constructor since this may hinder inheritance. Yet, it would not be overly difficult to apply it there too. |
Comment author: @alainfrisch
I was under the impression that default expressions are often constants (such as [], "", 0) or identifiers (Location.none). Is the function application case really common?
Can you elaborate on that? |
Comment author: @garrigue
In LablGtk, there are many cases where the default is looked up in the runtime. Also a default may be defined with respect to another argument: let f x ?(y=x+1) () = ... It is true that this pattern is less frequent in ocaml than in olabl, where optional arguments didn't require a non-labeled argument to be after, but there are still occurrences of that.
I just meant that if you do that, you need to pass many optional arguments whenever you inherit, if you want to keep the customizability, whereas there are more clever ways to do that, such as value fields. But I admit this is not a very good argument. |
Comment author: @xclerc Not the best idea I ever had, but I can remember using the |
Comment author: @damiendoligez From the reference manual (section 6.7.1/function definitions): Functions of the form fun ?lab:( pattern = expr0 ) -> expr are equivalent to Note that last sentence: it is explicitly unspecified. Do we want to change the behaviour? Do we want to make the documentation more explicit? |
Comment author: @alainfrisch My fault, the documentation is indeed accurate. I still have a preference for making the evaluation order well-defined by not pushing the evaluation unless the expression is provably pure. But it seems some projects rely heavily on the optimization, including for potentially impure expressions, so I won't push strongly in this direction. |
Comment author: Julien Signoles I think the current documentation is precise enough. In Frama-C, there are a bunch of functions of the form in which the result of (current ()) depends on when it is evaluating. However, such functions are never partially applied, so the behavior remains specified according to the current semantics. |
Original bug ID: 5975
Reporter: @alainfrisch
Assigned to: @alainfrisch
Status: resolved (set by @alainfrisch on 2015-12-03T12:57:35Z)
Resolution: suspended
Priority: normal
Severity: minor
Target version: 4.03.0+dev / +beta1
Category: ~DO NOT USE (was: OCaml general)
Related to: #6610
Monitored by: @gasche "Julien Signoles" @hcarty @yakobowski
Bug description
The expression provided to compute the default value of optional arguments are pushed inside lambdas in the typedtree (cf Translcore.push_defaults). I assume this is done for performance purposes (efficient compilation of n-ary urried functions), but it can lead to surprising semantics (and I did not find it documented in the manual):
let f ?(x = print_endline "XXX"; 1) y = function z -> x + y + z;;
let g = f 2;;
g 1;;
g 2;;
will print XXX twice.
The default expressions are not pushed under "functions" with several branches:
let f ?(x = print_endline "XXX"; 1) = function Some y -> (function z -> x + y + z) | None -> (function z -> x + z);;
let g = f (Some 2);;
Here, "XXX" is printed directly (not each time g is called), contrary to:
let f ?(x = print_endline "XXX"; 1) = function Some y -> (function z -> x + y + z);;
let g = f (Some 2);;
Shouldn't we restrict the optimization to cases where the default expression is pure?
As a side note: Typeclass puts the "Default" mode on the generated let binding for default expressions on class arguments, but it is not recognized in a special way. I.e.:
class c ?(x = print_endline "XXX"; 1) y z = object method m = x + y + z end;;
let _ = new c 2
will print "XXX" immediately.
The text was updated successfully, but these errors were encountered: