|Anonymous | Login | Signup for a new account||2016-10-21 18:44 CEST|
|Main | My View | View Issues | Change Log | Roadmap|
|View Issue Details|
|ID||Project||Category||View Status||Date Submitted||Last Update|
|0005975||OCaml||OCaml general||public||2013-04-04 13:49||2015-12-03 13:57|
|Priority||normal||Severity||minor||Reproducibility||have not tried|
|Target Version||4.03.0+dev / +beta1||Fixed in Version|
|Summary||0005975: Pushing the evaluation of default expression for optional arguments is not specified|
|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;;
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.
|Tags||No tags attached.|
Indeed, the documentation is lacking here.
The cause for this optimization is that the standard semantics would lead to a code size explosion with the native code compiler, in particular for LablTk.
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.
> 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 was under the impression that default expressions are often constants (such as , "", 0) or identifiers (Location.none). Is the function application case really common?
> It is not a good idea to use many optional arguments in an object constructor since this may hinder inheritance.
Can you elaborate on that?
> 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?
In LablGtk, there are many cases where the default is looked up in the runtime.
For instance, the default color palette.
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.
> > It is not a good idea to use many optional arguments in an object constructor since this may hinder inheritance.
> Can you elaborate on 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.
In LablGTK, my policy is to define the constructors as independent functions, outside of the class, as this avoids this problem, which stems from the fact ocaml only allows one constructor per class.
But I admit this is not a very good argument.
If there is some need, I'm ready to optimize this case too.
Not the best idea I ever had, but I can remember using the
current semantics to automatically generate a new identifier
if none is provided.
From the reference manual (section 6.7.1/function definitions):
Functions of the form fun ?lab:( pattern = expr0 ) -> expr are equivalent to
fun ?lab:ident -> let pattern = match ident with Some ident -> ident | None -> expr0 in expr
where ident is a fresh variable. When expr0 will be evaluated is left unspecified.
Note that last sentence: it is explicitly unspecified. Do we want to change the behaviour? Do we want to make the documentation more explicit?
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.
Julien Signoles (reporter)
I think the current documentation is precise enough.
In Frama-C, there are a bunch of functions of the form
let f ?(project=current ()) other_arguments = ...
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.
|2013-04-04 13:49||frisch||New Issue|
|2013-04-08 01:54||garrigue||Note Added: 0009042|
|2013-04-08 10:25||frisch||Note Added: 0009043|
|2013-04-08 15:06||garrigue||Note Added: 0009045|
|2013-04-23 15:33||xclerc||Note Added: 0009196|
|2013-06-09 11:14||xleroy||Relationship added||related to 0006020|
|2013-06-09 11:14||xleroy||Relationship deleted||related to 0006020|
|2013-06-18 15:48||doligez||Note Added: 0009548|
|2013-06-18 15:48||doligez||Status||new => acknowledged|
|2013-06-18 15:48||doligez||Target Version||=> 4.02.0+dev|
|2013-06-18 17:04||frisch||Note Added: 0009549|
|2013-06-20 14:54||Julien Signoles||Note Added: 0009588|
|2013-07-12 18:15||doligez||Target Version||4.02.0+dev => 4.01.1+dev|
|2014-05-25 20:20||doligez||Target Version||4.01.1+dev => 4.02.0+dev|
|2014-07-30 14:05||doligez||Target Version||4.02.0+dev => 4.02.1+dev|
|2014-09-04 00:25||doligez||Target Version||4.02.1+dev => undecided|
|2014-09-24 18:39||doligez||Target Version||undecided => 4.03.0+dev / +beta1|
|2014-12-17 23:52||doligez||Relationship added||related to 0006610|
|2015-12-03 13:57||frisch||Status||acknowledged => resolved|
|2015-12-03 13:57||frisch||Resolution||open => suspended|
|2015-12-03 13:57||frisch||Assigned To||=> frisch|
|Copyright © 2000 - 2011 MantisBT Group|