Статьи

Мой первый макрос Clojure

Мои ноги промокли от Clojure

Я начал жесткое кодирование в  Project Plugh . Я работаю над переносом нескольких концепций Lift на Clojure, когда создаю кометные возможности Lift в Clojure, чтобы я мог передавать данные в браузер.

Задний план… PartialFunction

В Scala есть PartialFunction

Ключевым понятием для PartialFunctions является «… это унарная функция, в которой домен не обязательно включает все значения типа A.»

PartialFunction Очень полезна возможность проверить, содержит  ли домен определенное значение. pf.isDefinedAt(x) позволяет проверить, определена ли функция с заданным значением  x.

Но a  PartialFunction является подклассом  Function, поэтому  PartialFunctions может применяться:

pf(x)

Компилятор Scala возьмет шаблон и превратит его в  PartialFunction:

def pf: PartialFunction[String, Number] = 
{
case "" => 0 // special case blank to zero
case x if isInt(x) => x.toInt
case x if isDouble(x) => x.toDouble
case x if isBigInt(x) => asBigInt(x)
}

Другое свойство  PartialFunction состоит в том, что они могут быть составлены:

pf = pf1 orElse pf2 orElse pf3 // pf isDefinedAt any place 
// any of the partial functions are defined

Мы PartialFunctions широко используем  в Lift, чтобы разрешить выбор, должен ли конкретный URL обслуживаться Lift, должен ли он обслуживаться определенным обработчиком REST и т. Д. Например, определение маршрута REST в Lift:

serve {
case "api" :: "user" :: AsLong(userId) :: _ GetJson _ => 
User.find(userId).map(_.toJson)
}

Поскольку я изучал Clojure в рамках подготовки к презентации в Strange Loop и как часть  нового проекта, над которым я работал , я стремлюсь привнести лучшие вещи в Lift в код Clojure, который я пишу.

В Clojure

Материал соответствия Clojure   довольно изящен. Особенно мне нравится , как вы можете извлечь значения из карты (это  так  гораздо более мощным , что согласование в Scala модель, даже … но исключить его я отвлекся).

Итак, я написал макрос:

(defmacro match-func [& body] `(fn [~'x] (match [~'x] ~@body)))

Это создает функцию, которая является применением сопоставления с параметром, поэтому:

((match-func [q :guard even?] (+ 1 q) [z] (* 7 z)) 33)
;; 231

Оказывается, шаблонизатор Clojure будет извлекать значения в несвязанные переменные. Но связанные переменные проверяются … это означает, что:

 ((match-func [[x y]] (+ x y)) [4 5])

Оказывается, это проблема, потому что  x она связана с  match-func макросом … поэтому нам нужно перейти  x на что-то другое. Итак, мы должны изменить переменную  x на что-то другое:

 (defmacro match-func [& body]
"Create a function that does pattern matching."
`(fn [x#] (match [x#] ~@body)))

isDefinedAt

Итак, как мы можем проверить, соответствует ли шаблон определенному значению?

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

Ответ арности. Scala имеет функции с определенной арностью. Оказывается, что Clojure может иметь единственную функцию, которая ведет себя по-разному в зависимости от арности вызова. Ура!

Итак, макрос выглядит так:

(defmacro match-pfunc [& body]
"Create a partial function that does pattern matching."
(let [rewrite (mapcat (fn [x] [(first x) true]) (partition 2 body))]
`(fn ([x#] (match [x#] ~@body))
([x# y#]
(cond
(= :defined? x#)
(match [y#] ~@rewrite)
(= :body x#)
'(~@body))))))

Это дает нам функцию, которую можно вызывать с помощью одного параметра:

(pf 44)

И это может быть вызвано с 2 параметрами:

(pf :defined? 44)

И я добавил возможность получить тело оригинала, чтобы я мог добавить  orElse функцию, которая фактически создаст новую,  PartialFunction то есть компиляцию составленных шаблонов, чтобы шаблоны были скомпилированы более эффективно.

Первый палец в воде

Ага. Я думаю, что Clojure довольно мощный. С помощью макросов я добавил в Clojure одну из самых удивительно мощных языковых функций Scala в несколько строк.

Вода чувствует себя довольно хорошо до сих пор.