12

"Introduction to Caml" says

Note, in Caml it is better to use Curried function definitions for multiple-argument functions, not tuples.

when comparing 'a -> 'b -> 'c calling conventions to 'a * 'b -> 'c.

When working with SML/NJ I got used to using tuple types for both input and output : ('a * 'b) -> ('c * 'd) so using tuples to express multiple inputs seems symmetric with the way I express multiple outputs.

Why is currying recommended for OCaml function declarations over tuple arguments? Is it just the greater flexibility that comes with allowing currying/partial evaluation, or is there some other benefit that derives from implementation details of the OCaml compiler?

9
  • 3
    The choice of currying most functions in Caml-light and subsequent versions is explained in the report "The ZINC experiment: an economical implementation of the ML language". One thing I remember is that with the proper evaluation scheme (described in the report), a curried function requires no allocation in order to be called. caml.inria.fr/pub/papers/xleroy-zinc.ps.gz Commented May 19, 2012 at 16:43
  • @PascalCuoq, whereas a tuple needs to be allocated, unpacked, and then GCed? Commented May 19, 2012 at 16:45
  • 1
    That's another question, but to answer, no, you shouldn't. Last time I checked it didn't even optimize the allocation of a, b in match a, b with x, y -> .... If you wish to check by yourself, I found that reading the x86 assembly generated by ocamlopt -S was convenient for me because I didn't have to learn a new representation. Commented May 19, 2012 at 16:56
  • 3
    Just to be clear, tupled functions don't usually require allocation either in compilers that optimise for them, as is the case in most implementations of SML. N-ary argument/results tuples are typically compiled to N arguments/results as part of standard flattening optimisations. Only when an argument/result tuple is given or used in a first-class manner additional boxing/unboxing takes place (i.e. exactly in those cases that you cannot express in curried form). Commented May 19, 2012 at 19:07
  • 1
    Pascal, your first comment is interesting, sourced, and an excellent completent to Andreas answer. Why did you not post it as an answer? I am quite sure that match a,b with x,y -> ... is optimized; in fact we even recently discussed an extension of this optimization, on which feedback would be welcome. Commented May 19, 2012 at 19:46

3 Answers 3

8

I think a lot of it is convention -- standard library functions in OCaml are curried, whereas in Standard ML they are generally not except for some higher-order functions. However, there is one difference baked into the language: the operators (e.g. (*)) are curried in OCaml (e.g. int -> int -> int); whereas they are uncurried in Standard ML (e.g. op* can be (int * int) -> int). Because of that, built-in higher-order functions (e.g. fold) also take a function that is curried in OCaml and uncurried in Standard ML; that means for your function to work with that, you need to follow the respective convention, and it follows from there.

Sign up to request clarification or add additional context in comments.

2 Comments

Good point. There is another instance where the choice is baked in: datatype constructors. In SML they are tupled, in Haskell they are curried. Strangely enough, in OCaml they are tupled, which is a bit of a mismatch with the rest of the language.
The deeper meaning behind datatype constructors being tupled is that a tuple is an anonymous special-case data constructor.
5

Yes, it is mainly the notational convenience and the flexibility to do partial application. Curried functions are idiomatic in OCaml, and the compiler is likely to optimise them somewhat better than tupled functions (whereas SML compilers typically optimise for tuples).

The pros of tupling are the argument/result symmetry you mention (which is especially useful when composing functions) and perhaps the notational familiarity (at least for people coming from the non-functional world).

Comments

3

Some comment about the optimisation in OCaml.

In OCaml, i noticed that tuples are always allocated when passing them as argument. Even if allocating in the primary heap is fast in ocaml, it is of course longer than doing nothing. Thus each time you pass a tuple as argument, there is some time spent to the allocation and filling of the tuple.

I expected that the ocaml compiler would be optimizing cases where it isn't needed to build the tuple. For instance, when you inline the called function, you may only use the tuple components and not the tuple itself. Thus the tuple could just be ignored. Unfortunately in this case OCaml doesn't remove the useless tuple and still perform the allocation. For this reason, it may not be a good idea to use tuples in critical sections of code.

1 Comment

I think this is no longer true. "OCaml is smarter than I thought" says "I'd prevented the inlining, but there was still no allocation! Why? Well, it turns out that OCaml can optimize a tuple-taking function to get the elements of the tuple passed in via registers, which is exactly what happened. And again, the compiler realized that no allocation was required."

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.