using a loop
You need some sort of loop
to collect each arg
from the lambda
-
(define (curry-n num f)
(let loop
((n num)
(args null))
(lambda ((arg null))
(cond ((= n 0)
(apply f (reverse args)))
((= n 1)
(apply f (reverse (cons arg args))))
(else
(loop (- n 1) (cons arg args)))))))
curry
should always return a procedure, so you can see loop
will always return a lambda
, even for the num = 0
case. Each arg
is cons
'd onto args
, creating a reversed list of arguments. This is why we reverse
the args
before apply
ing the user-supplied procedure, f
.
It works like this -
(define (hello)
(println "hello world"))
((curry-n 0 hello))
"hello world"
((((curry-n 3 +) 10) 20) 30)
60
using delimited continuations
In the spirit of sharing learning exercises, take some time to review this example using delimited continuations -
(require racket/control)
(define (curry-3 f)
(reset (f (shift k k) (shift k k) (shift k k))))
(define (hello a b c)
(printf "hello world ~a ~a ~a\n" a b c))
((((curry-3 hello) 10) 20) 30)
hello world 10 20 30
To get curry-n
all we need to do is build a list of n
continuations!
(require racket/control)
(define (curry-n n f)
(reset (apply f (build-list n (lambda (_) (shift k k))))))
And it works just like our first one -
(define (hello a b c)
(printf "hello world ~a ~a ~a\n" a b c))
((((curry-n 3 hello) 10) 20) 30)
"hello world 10 20 30"
((((((curry-n 5 +) 10) 20) 30) 40) 50)
150
You can visualize the process as working something like this, where each __
is a "hole" to fill -
(f __ __ __)
\
\_ (lambda (x)
(f x __ __))
\
\_ (lambda (y)
(f x y __))
\
\_ (lambda (z)
(f x y z))
The only difference is there are n
holes to fill, so we build a list of n
holes and apply
-
(build-list 3 (lambda (_) (shift k k)))
(list __
\
\_ (lambda (x)
(list x __
\
\_ (lambda (y)
(list x y __
\
\_ (lambda (z)
(list x y z))
(apply f (list x y z)
scheme
I didn't notice you were doing this work in Scheme. We can define build-list
-
(define (build-list n f)
(let loop
((m 0))
(if (>= m n)
'()
(cons (f m) (loop (+ m 1))))))
And we can use Olivier Danvy's original implementation of shift/reset,
(define-syntax reset
(syntax-rules ()
((_ ?e) (reset-thunk (lambda () ?e)))))
(define-syntax shift
(syntax-rules ()
((_ ?k ?e) (call/ct (lambda (?k) ?e)))))
(define *meta-continuation*
(lambda (v)
(error "You forgot the top-level reset...")))
(define abort
(lambda (v)
(*meta-continuation* v)))
(define reset-thunk
(lambda (t)
(let ((mc *meta-continuation*))
(call-with-current-continuation
(lambda (k)
(begin
(set! *meta-continuation* (lambda (v)
(begin
(set! *meta-continuation* mc)
(k v))))
(abort (t))))))))
(define call/ct
(lambda (f)
(call-with-current-continuation
(lambda (k)
(abort (f (lambda (v)
(reset (k v)))))))))
Our curry-n
can stay the same -
(define (curry-n n f)
(reset (apply f (build-list n (lambda (_) (shift k k))))))
((((((curry-n 5 +) 10) 20) 30) 40) 50)
150