From: mfx@cs.tu-berlin.de (Markus Freericks)
Subject: Re: Why curry? (Re: Why functional languages?)
Organization: Technical University of Berlin, Germany
	<TMB.92Oct4181821@arolla.idiap.ch> <1992Oct6.141724.29356@kth.se>
Date: Wed, 7 Oct 1992 09:56:29 GMT

In article <1992Oct6.141724.29356@kth.se> kff@dentex.tds.kth.se (Karl-Filip Faxen) writes:
   In article <TMB.92Oct4181821@arolla.idiap.ch>, tmb@arolla.idiap.ch (Thomas M. Br
   euel) writes:
   |> 
   |> Why _do_ people continue to use curried syntax when they just want a
   |> function with several arguments? It's difficult to optimize for the
   |> compiler, 

   That is not correct (and I should know, I'm writing a Haskell 
   compiler :-). First, it is not particularly difficult to generate
   good code for calls to curried functions (that is, functions with
   types like (a -> b -> c -> d -> e)). Every function is represented
   as a lambda-expression (e.g. (\x.\y.\z. e)), and in a compiled graph
   reduction implementation (e.g. the G machine and its derivates) the
   number of nested lambda bindings around the body of the function 
   (3 in this case) gives the precise number of arguments that the 
   compiled code for the function needs to in order to execute (partial
   applications are represented as a thunk (or closure) containing a 
   pointer to the code of the supercombinator and a list of the (still 
   to few) arguments it has been applied to). Now, if we have a simple,
   first order function application (where the applied function is a 
   known supercombinator) we can look up the number of outermost lambdas,
   check if the function is applied with the right number of arguments
   and call an entrypoint that uses some efficient calling convention.

   Thus the curried style of function definition carries no additional
   cost in itself; it is calling "unknown" functions that cost, and that
   cost is regardless of the syntax used when defining the function.

Your argumentation is based on a graph reduction implementation. I
would think that such are only feasible for lazy languages, which SML
(which started the thread) is not. Otherwise, you are of course quite
right.

   Second, the tupling style also needs to be optimized, or you have to
   allocate the tuples explicitly in the heap and then explicitly
   destructure them immediately afterwards. This can of course be done,
   but it is not any simpler than the above mentioned optimization for 
   curried functions (I think :-). Perhaps some of the SML/NJ implementors
   can comment on this?

Well, if you have a CPS-based compiler, function call arguments will
usually be held in registers, only closures have to be created on the
heap (or on the stack, if they provably don't escape). And in such an
environment, tupling is much cheaper! (Again, it is of course true
that a very naive approach would allocate tuples as data objects on
the heap.)

   |> it gives uninformative type signatures (is "'a -> 'a -> 'a"
   |> intended to be a function of one argument returning a function, or is
   |> it intended to be a function of two arguments returning a "'a"?),

   It is both. You are free to apply it to two arguments to get a "'a" in 
   one place and apply it to one argument and get a function with type 
   "'a -> 'a" that you e.g. map over a "'a list" in another place. I do
   these things all the time.

Well, in my SML programs (I am writing a partial evalutor) i tend to
have functions of 4-5 parameters, and missing parameters are one of
the most typical typing errors I make. Of course, type check would
catch most of these in a currying language, too. Oh yes: a lot of
these are functions that are *never* applied partially.

   |> it's not what most programmers are used to, 

   Sorry about this, but I _have_ to remind you that most programmers are
   not used to either Haskell or SML at all, but rather to COBOL, BASIC
   and FORTRAN. The question is: What is easiest to get used to, and, once
   gotten used to, most productive? And as that is difficult to measure,
   this point boils down to a matter of taste, and taste is not to be 
   argued about.

Well, for me the question is: does a language *force* me to use
curried functions, or does it offer tupled functions, too? For me,
tupling is a safety device, a little comparable to typing, that makes
it possible to say "this function should be called with so many
arguments". A language that does not support this device (or makes it
overtly expensive, e.g. by explicitly creating/desctructing tupel
objects) lacks a feature that makes life easier.

   |> and it seems to be difficult to indent automatically as well.

   In an Emacs mode, perhaps. In a pretty printer, certainly not (I've
   written one).

Has anybody yet written a decent SML emacs mode? The one that comes
with SML/NJ is, while not broken, quite irritating at times.

   Cheers,
	    Karl-Filip

Greetings,
	Markus

   ------------------------------------------------------------
   Make the frequent case fast, and the fast case frequent!

-- 
Markus Freericks	| email: mfx@cs.tu-berlin.de	
TU Berlin Sekr.FR 2-2 	| phone: +49-30-314-21390
Franklinstr.28/29	| priv.: +49-30-872523
D-1000 Berlin 10	|"Inertia makes the world go 'round."