;;; Converting a function of multiple arguments into a series of functions of one argument ;;; is known as Currying, after Haskell Curry. In a programming language where functions ;;; can make and return other functions, this technique is very easy to demonstrate. ;; Imagine that in an expression (* n m), a function of two arguments, the first argument is ;; held at a constant value, say, n=2. Then (* 3 m) can be thought of as a function of one ;; argument, which is tripled by this function. (setq mul (lambda (x) (lambda (y) (* x y)))) ; curried multiplication (closure (t) (x) #'(lambda (y) (* x y))) ; when called, this will return a function (funcall (funcall mul 3) 2) ; all of this to do 3*2 6 (funcall mul 3) ; this function could be called "triple". It returns a function every time it's called! (closure ((x . 3) t) (y) (* x y)) (setq triple (funcall mul 3)) ; in fact, let's call it that :) (closure ((x . 3) t) (y) (* x y)) (mapcar triple '(1 2 3 4 5 6 7)) (3 6 9 12 15 18 21) ;; Triple is called a _partial application_ of mul (or curried *), meaning that that fixing the ;; first argument of mul produces a function of one argument out of the two-argument * ;;; ---- Interlude ----- ;; I'd like to type less, so I am going to define a shorthand way of calling eLisp's disassembler. ;; This is a _macro_ that will not evaluate disassemble-internal, but will only make a list ;; ready for the evaluator, with the value of "f" pasted into the list. ;; Note that 2 and nil evaluate to themselves and need not be quoted, but 'disassemble-internal ;; must be quoted. (defmacro dis (f) (list 'disassemble-internal f 2 nil)) dis ;; macroexpand-1 expands just one level of this macro shorthand, showing what will be handed ;; to the evaluator. (macroexpand-1 '(dis mul)) (disassemble-internal mul 2 nil) ;; There is an even more convenient shorthand that uses the backtick ` instead of (list ..) ;; and , to indicate the parameters to be substituted. Everything else is verbatim. (defmacro dis (f) `(disassemble-internal ,f 2 nil)) dis (macroexpand-1 '(dis mul)) (disassemble-internal mul 2 nil) ;;; ----- end interlude ------ ;; So let's disassemble the partial application: (dis (funcall mul 3)) doc: ... args: (arg1) ; [arg1] 0 constant 3 ; [3 arg1] 1 stack-ref 1 ; [arg1 3 arg1] 2 mult ; [arg1*3 arg1] 3 return nil ;; and the function-making mul function itself: (dis mul) doc: ... args: (arg1) ; [arg1] 0 constant make-closure ; [make-closure arg1] 1 constant ; [ make-closure arg1] doc: ... args: (arg1) ; starts with its own stack [arg1'] 0 constant V0 ; [ arg1'] 1 stack-ref 1 ; [arg1' arg1'] 2 mult ; [arg1'*x arg1'] 3 return 2 stack-ref 2 ; [arg1 make-closure arg1] 3 call 2 ; [(make-closure arg1) arg1] 4 return nil ;; byte-compile reveals the constants vectors and bytecode strings (byte-compile mul) #[257 "\300\301\"\207" [make-closure #[257 "\300_\207" [V0] 3 " (fn Y)"]] 4 " (fn X)"] ;; if you look carefully: ;; \300 is the bytecode for "constant 0", i.e., "push the pointer to the first element of this closure's const vector", make-closure is the outer function bytecode, V0 is the inner function bytecode. ;; \301 is the bytecode for "constant 1" ;; '^B' is "stack-ref 2", '^A' is "stack-ref 1" ;; '_' is mult, '\"' is "call 2" ;; \207 is return in both ;; More about emacs bytecodes and constant vectors: ;; https://nullprogram.com/blog/2014/01/04/ ;;; Let's look at our previous closure example from elisp's own manual: (setq mytracker (let ((cnt 1)) ; <<-- this value will be 'closed over' (lambda () (setq cnt (1+ cnt))))) (closure ((cnt . 1) t) nil (setq cnt (1+ cnt))) (funcall mytracker) 2 (funcall mytracker) 3 (dis mytracker) ;; comments for each instruction show the stack as it will be after the instruction executes args: nil 0 constant (3) ; [->(3 nil)] <<-- this is reference to a cons cell with a car of 3 1 dup ; [->(3 nil) ->(3 nil)] 2 car-safe ; [3 ->(3 nil)] 3 add1 ; [4 ->(3 nil)] 4 setcar ; [4] 5 return nil (funcall mytracker) 4 (dis mytracker) ;; comments for each instruction show the stack as it will be after the instruction executes args: nil ; [] 0 constant (4) ; [->(cons 4 nil)] 1 dup ; [->(cons 4 nil) [->(cons 4 nil)] 2 car-safe ; [4 ->(cons 4 nil)] 3 add1 ; [5 ->(cons 4 nil)] 4 setcar ; [5] 5 return nil mytracker (closure ((cnt . 4) t) nil (setq cnt (1+ cnt))) (funcall mytracker) 5 mytracker (closure ((cnt . 5) t) nil (setq cnt (1+ cnt))) ;; and now we see 5 in the associated constants vector (dis mytracker) args: nil 0 constant (5) ; <<-- 5 in 1 dup 2 car-safe 3 add1 4 setcar 5 return nil ;;;; Currying will be extremely useful in lambda calculus!