Mantis Bug Tracker

View Issue Details Jump to Notes ] Issue History ] Print ]
IDProjectCategoryView StatusDate SubmittedLast Update
0005975OCamlOCaml generalpublic2013-04-04 13:492013-07-12 18:15
Reporterfrisch 
Assigned To 
PrioritynormalSeverityminorReproducibilityhave not tried
StatusacknowledgedResolutionopen 
PlatformOSOS Version
Product Version 
Target Version4.01.1+devFixed in Version 
Summary0005975: Pushing the evaluation of default expression for optional arguments is not specified
DescriptionThe 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.
TagsNo tags attached.
Attached Files

- Relationships

-  Notes
(0009042)
garrigue (manager)
2013-04-08 01:54

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.
(0009043)
frisch (developer)
2013-04-08 10:25

> 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?
(0009045)
garrigue (manager)
2013-04-08 15:06

> 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.
(0009196)
xclerc (developer)
2013-04-23 15:33

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.
(0009548)
doligez (administrator)
2013-06-18 15:48

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?
(0009549)
frisch (developer)
2013-06-18 17:04

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.
(0009588)
Julien Signoles (reporter)
2013-06-20 14:54

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.

- Issue History
Date Modified Username Field Change
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


Copyright © 2000 - 2011 MantisBT Group
Powered by Mantis Bugtracker