I'm learning about the macro system in Scheme and I thought implementing curried functions would be a good start. This is what I cooked up:

(define-syntax function
    (syntax-rules ()
        ((_ () body ...) (lambda () body ...))
        ((_ (param) body ...) (lambda (param) body ...))
        ((_ (param_1 param_2 params ...) body ...) (lambda (param_1 . rest)
            (let ((k (function (param_2 params ...) body ...)))
                (if (null? rest) k (apply k rest)))))
        ((_ name params body ...) (define name (function params body ...)))))

This code works as expected to. For example I can define an add function as follows:

(function add (x y) (+ x y))

Then I can call it normally:

(add 2 3) ; => 5

In addition I can easily partially apply it:

(map (add 10) '(2 3 5 7)) ; => (12 13 15 17)

Now I'm contemplating to allow functions with rest parameters to be curried. So I added a new syntax rule:

((_ (param . params) body ...) (lambda (param . params) body ...))

Unfortunately when I try to create a function using this rule it gives me an error:

(function add (x . y) (apply + `(,x ,@y)))

This is the error message:

Error: invalid syntax in macro form: (x . y)

    Call history:

    <eval>    (##sys#= len7 0)
    <eval>    (loop11 (##sys#cdr l6) (##sys#+ len7 -1))
    <eval>    (##sys#cdr l6)
    <eval>    (##sys#+ len7 -1)
    <eval>    (##sys#= len7 0)
    <eval>    (loop11 (##sys#cdr l6) (##sys#+ len7 -1))
    <eval>    (##sys#cdr l6)
    <eval>    (##sys#+ len7 -1)
    <eval>    (##sys#= len7 0)
    <eval>    (##sys#eq? l6 (quote ()))
    <eval>    (##sys#car tail15)
    <eval>    (##sys#cdr tail15)
    <eval>    (##sys#cons (rename14 (##core#syntax lambda)) (##sys#cons param body))
    <eval>    (rename14 (##core#syntax lambda))
    <eval>    (##sys#cons param body)
    <syntax>      (##core#lambda add (x . y) (apply + (quasiquote ((unquote x) (unquote-splicing y))))) <-

What am I doing wrong?

有帮助吗?

解决方案

[The comment is correct; this answer is not currying, it is partial evaluation.]

Just so you know, you don't need to use define-syntax to support currying. Generally using syntax when you don't need to is frowned upon because 1) syntax introduces different evaluation rules and 2) syntax can't be used as a value.

Here are two implementations, one for (left) curry and one for right curry:

  (define (curry func . curry-args)
    (lambda args
      (apply func (append curry-args args))))

  (define (rcurry func . curry-args)
    (lambda args
      (apply func (append args curry-args))))

Use this as for example:

> (define add-5 (curry + 5))
> (add-5 5)
10

其他提示

You don't say what version of Scheme you're using. It appears that it doesn't support 'dot' patterns in macros.

In Racket, it looks like your code works:

#lang racket

(define-syntax function
    (syntax-rules ()
      ((_ () body ...) (lambda () body ...))
      ((_ (param) body ...) (lambda (param) body ...))
      ((_ (param_1 param_2 params ...) body ...) (lambda (param_1 . rest)
                                                   (let ((k (function (param_2 params ...) body ...)))
                                                     (if (null? rest) k (apply k rest)))))

      ((_ (param . params) body ...) (lambda (param . params) body ...))
      ((_ name params body ...) (define name (function params body ...)))))

(function add (x . y) (apply + `(,x ,@y)))

(add 2 3)

running this produces the answer

5

.

BTW, I think I would have written this as two macros; the dual-purposing of the name 'function' is a bit sketchy... :)

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top