Как реализовать лямбда как функцию, называемую "лямбдой" в Clojure?

Я хотел бы иметь возможность определять lambdas, используя общий синтаксис Lisp, в Clojure. Например:

(lambda (myarg)
  (some-functions-that-refer-to myarg))

Это должно привести к тому, что:

#(some-functions-that-refer-to %)

В моем случае, я знаю, что у меня всегда будет один аргумент, поэтому, возможно, это упрощает. (Но его можно назвать чем угодно - "myarg" или что-то еще.)

Я подозреваю, что работоспособным решением является "(defmacro lambda...".Если это так, я не уверен в лучшем способе продолжения. Как чисто перевести имя arg на%? И как в итоге правильная функция?

Или, есть ли более простое решение, чем писать собственный макрос, который на самом деле повторно реализует Clojure 's... lambda?

Ответы

Ответ 1

#(foo %) является просто сокращением для (fn [arg] (foo arg)). Нет причин писать макрос, который расширяется до #(...). Вся конструкция % в #(...) в любом случае разлагается на gensyms.

user> `#(foo % %1 %2)
(fn* [user/p1__1877 user/p2__1878] 
  (user/foo user/p1__1877 user/p1__1877 user/p2__1878))

Если вы когда-либо пишете макрос, который расширяется для создания анонимных функций, вы можете просто расширить их до форм fn. В вашем случае вам следует просто использовать fn напрямую и пропустить макросы. fn - Clojure lambda.

Разница между (fn [] ...) и (lambda () ...) в этом случае заключается в том, что "fn" короче, чем "lambda", а fn берет вектор для своих привязок, тогда как lambda принимает список. Если вы используете Clojure, вам придется привыкнуть к этому в конечном счете, потому что векторы всегда используются для коллекций привязок, во всех формах do и в for и binding и т.д. Обоснование так как я понимаю, что списки используются для вызовов функций или вызовов макросов, а векторы используются для вещей, которые не являются вызовами (например, списки символов для привязки). Вероятно, это облегчает сканирование кода визуально, чем списки - все-на-пути вниз. Clojure не является общим Lisp, и вы испытаете боль, если попытаетесь заставить его быть.

Если вы действительно, действительно хотели сделать это, просто сказать, что вы сделали:

user> (defmacro lambda [args & body]
        `(fn ~(vec args) [email protected]))
user> ((lambda (x) (println x)) "foo")
foo
nil

Это не позволяет вам помещать docstring или метаданные в вашу функцию, среди прочего. Я не думаю, что вы захотите использовать это в реальной программе Clojure.