Функции каррирования в Scheme с использованием макросов

Я изучаю макросистему в Scheme, и я подумал, что реализация функций с карри будет хорошим началом. Вот что я приготовил:

(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 ...)))))

Этот код работает, как и ожидалось. Например, я могу определить add функционировать следующим образом:

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

Тогда я могу назвать это нормально:

(add 2 3) ; => 5

Кроме того, я могу легко частично применить его:

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

Теперь я размышляю над тем, чтобы позволить функциям с параметрами rest быть каррированными. Поэтому я добавил новое правило синтаксиса:

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

К сожалению, когда я пытаюсь создать функцию, используя это правило, выдает ошибку:

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

Это сообщение об ошибке:

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))))) <-

Что я делаю неправильно?

2 ответа

Решение

[Комментарий правильный; этот ответ не карри, это частичная оценка.]

Просто чтобы вы знали, вам не нужно использовать define-syntax поддержать карри. Обычно использование синтаксиса, когда вам не нужно, не одобряется, потому что 1) синтаксис вводит различные правила оценки и 2) синтаксис не может использоваться в качестве значения.

Вот две реализации, одна для (левого) карри и одна для правого карри:

  (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))))

Используйте это как например:

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

Вы не говорите, какую версию Схемы вы используете. Похоже, что он не поддерживает точечные шаблоны в макросах.

В Racket, похоже, ваш код работает:

#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)

выполнение этого дает ответ

5

,

Кстати, я думаю, что написал бы это как два макроса; двойное назначение имени 'функция' немного схематично...:)

Другие вопросы по тегам