Функции более высокого порядка в Clojure

Clojure потрясающий, мы все это знаем, но дело не в этом. Мне интересно, каков идиоматический способ создания и управления функциями более высокого порядка в Haskell-подобном способе. В Clojure, я могу сделать следующее:

(defn sum [a b] (+ a b))

но (sum 1) не возвращает функцию: это вызывает ошибку. Конечно, вы можете сделать что-то вроде этого:

(defn sum
  ([a] (partial + a)) 
  ([a b] (+ a b)))

в этом случае:

user=> (sum 1)
#<core$partial$fn__3678 clojure.core$partial$fn__3678@1acaf0ed>
user=> ((sum 1) 2)
3

но это не похоже на правильный путь. Есть идеи?
Я не говоря о реализации

3 ответов


кто-то уже implememented этот в группе Clojure. Вы можете указать, сколько args имеет функция, и она будет карри для вас, пока не получит столько.

Почему это не происходит по умолчанию в Clojure является то, что мы предпочитаем variadic функции автоматического каррированные функции, я полагаю.


Я немного поиграл с функциями, предложенными amalloy. Мне не нравится явная спецификация количества аргументов для карри. Поэтому я создал свой пользовательский макрос. Это старый способ определить функцию высокого порядка:

(defn-decorated old-sum
  [(curry* 3)]
  [a b c]
  (+ a b c))

Это мой новый макрос:

(defmacro defn-ho
  [fn-name & defn-stuff]
  (let [number-of-args (count (first defn-stuff))]
    `(defn-decorated ~fn-name [(curry* ~number-of-args)] ~@defn-stuff)))

и это новый неявный способ:

(defn-ho new-sum [a b c] (+ a b c))

как вы можете видеть, нет никаких следов (Карри) и других вещей, просто определите свою функцию currified как до.

ребята, как вы думаете? Идеи? Предложения? Тю!

Alfedo

Edit: я изменил макрос в соответствии с проблемой amalloy о docstring. Это обновленная версия:

(defmacro defhigh
  "Like the original defn-decorated, but the number of argument to curry on
  is implicit."
  [fn-name & defn-stuff]
  (let [[fst snd] (take 2 defn-stuff)
         num-of-args (if (string? fst) (count snd) (count fst))]
    `(defn-decorated ~fn-name [(curry* ~num-of-args)] ~@defn-stuff)))

мне не нравится оператор if внутри второй привязки. Есть идеи сделать его более лаконичным?


Это позволит вам делать то, что вы хотите:

(defn curry
  ([f len] (curry f len []))
  ([f len applied]
    (fn [& more]
      (let [args (concat applied (if (= 0 (count more)) [nil] more))]
        (if (< (count args) len)
          (curry f len args)
          (apply f args))))))

вот как его использовать:

(def add (curry + 2)) ; read: curry plus to 2 positions
((add 10) 1) ; => 11

условное с [nil] предназначен для обеспечения того, чтобы каждое приложение обеспечивало некоторый прогресс вперед в состояние Карри. За этим стоит длинное объяснение, но я нашел его полезным. Если вам не нравится этот бит, вы можете установить параметры, как:

[args (concat applied more)]

В отличие от JavaScript у нас нет способа узнать arity переданной функции, и поэтому вы должны укажите ожидаемую длину. Это имеет большой смысл в Clojure[Script], где функция может иметь несколько аритов.