Функции каррирования в 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
,
Кстати, я думаю, что написал бы это как два макроса; двойное назначение имени 'функция' немного схематично...:)