"Autocurrying?"

Started by kinghajj, February 15, 2008, 11:15:37 AM

Previous topic - Next topic

kinghajj

I've started to learn F# (a OCaml-like language for .NET), and one feature that really struck me is automatic currying. Here's a newLISP-syntax example of what I mean (it is not valid newLISP code):



(define (add-nums a b)
  (+ a b)
(setq add10 (add-nums 10))
(add10 3) ; => 13


Basically, when you "call" a function without supplying it every argument, it returns a curried function that expects the takes the next arguments.



Could something like this be added to newLISP? I realize that breaking current code is bad, but there could be an "autocurry" macro:



(define (add-nums a b)
  (+ a b))
(autocurry add-nums)


I've tried to implement an autocurry in newLISP, but hit a wall because it's difficult to differentiate between a "nil" passed by default and a "nil" passed by the caller.

Jeff

#1
If you create a factory-function macro, you can accept a lambda and have it wrap that lambda.  Any time a function declared with the macro is called with x arguments, the macro returns a curried function that has those x arguments.  That way you can grab (args) and just use that.



By the way, I'd love to see the result of your experiments.  I love OCaml.
Jeff

=====

Old programmers don\'t die. They just parse on...



http://artfulcode.net\">Artful code

Cyril

#2
Quote from: "kinghajj"Basically, when you "call" a function without supplying it every argument, it returns a curried function that expects the takes the next arguments.


You have underspecified the wanted behavior: if you call a function with supplying it every argument, do you want just to call it, or to return a null-ary function with can be called later? If second, than it can be easy done for any function:


(define (make-curry2 f)
  (letex (F f)
    (lambda ()
      (letex (A (args))
        (lambda ()
          (apply F (append 'A (args))))))))

> (setq add-nums2 (make-curry2 +))
> ((add-nums2 10 20) 30 40)
100


If first, then you must define what the the words "every argument" mean. Remember that function may be called with fewer or more arguments, and, say, there are no "right" number of arguments for built-in function "+".  If we limit ourself with user-defined (not built-in) functions, and declare that "right" number of arguments is number of arguments explicitly written in its definition, then the following works:


(define (make-curry1 f)
  (letex (F f)
    (lambda ()
      (if (< (length (args)) (length ((0 F) 0)))
        (letex (A (args))
          (lambda ()
            (apply F (append 'A (args)))))
        (apply F (args))))))

> (define (add-nums a b) (+ a b))
> (setq add-nums1 (make-curry1 add-nums))
> ((add-nums1 10) 20)
30
> (add-nums1 10 20)
30


Note: edited 9:10 am Moscow time. Has contained nasty bug before: wrong (0 F) instead of right ((0 F) 0). Sorry if you have read this before fix.



Note that this not work with "+" instead of "add-nums". And then think, why authors of Ocaml took so long time to introduce the concept of "default value of an argument" in their language (this feature appears in Ocaml 3.0 only). The task of joining default values and default curring in a single language is not an easy one. Whichever you implement first, the second will be a difficult quest. Most languages (including newlisp) have had default values first, Ocaml have had curring first.
With newLISP you can grow your lists from the right side!