Я начал жесткое кодирование в Project Plugh . Я работаю над переносом нескольких концепций Lift на Clojure, когда создаю кометные возможности Lift в Clojure, чтобы я мог передавать данные в браузер.
Фон … PartialFunction
В Scala есть PartialFunction
Ключевым понятием для PartialFunctions является «… это унарная функция, в которой домен не обязательно включает все значения типа A.»
Очень полезна возможность протестировать функцию PartialFunction чтобы увидеть, содержит ли домен определенное значение. pf.isDefinedAt(x) позволяет проверить, определена ли функция с заданным значением x .
Но PartialFunction является подклассом Function , поэтому можно применять PartialFunctions :
|
1
|
pf(x) |
Компилятор Scala возьмет шаблон и превратит его в PartialFunction :
|
1
2
3
4
5
6
7
|
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 — они могут быть составлены:
|
1
2
|
pf = pf1 orElse pf2 orElse pf3 // pf isDefinedAt any place // any of the partial functions are defined |
Мы широко используем PartialFunctions в Lift, чтобы позволить выбрать, должен ли Lift обслуживать конкретный URL, должен ли он обслуживаться определенным обработчиком REST и т. Д. Например, определяя маршрут REST в Lift:
|
1
2
3
4
|
serve { case "api" :: "user" :: AsLong(userId) :: _ GetJson _ => User.find(userId).map(_.toJson)} |
Поскольку я изучал Clojure в рамках подготовки к презентации в Strange Loop и как часть нового проекта, над которым я работал , я стремлюсь привнести лучшие вещи в Lift в код Clojure, который я пишу.
В Clojure
Материал соответствия Clojure довольно изящен. Мне особенно нравится, как вы можете извлекать значения из карты (это намного мощнее, чем сопоставление с образцом в Scala, даже при неприменении … но я отвлекся).
Итак, я написал макрос:
|
1
|
(defmacro match-func [& body] `(fn [~'x] (match [~'x] ~@body))) |
Это создает функцию, которая является применением сопоставления с параметром, поэтому:
|
1
2
|
((match-func [q :guard even?] (+ 1 q) [z] (* 7 z)) 33);; 231 |
Оказывается, шаблонизатор Clojure будет извлекать значения в несвязанные переменные. Но связанные переменные проверяются … это означает, что:
|
1
|
((match-func [[x y]] (+ x y)) [4 5]) |
Оказывается, это проблема, потому что x связан в макросе match-func … поэтому нам нужно заменить x на что-то другое. Итак, мы должны изменить переменную x на что-то другое:
|
1
2
3
|
(defmacro match-func [& body] "Create a function that does pattern matching." `(fn [x#] (match [x#] ~@body))) |
isDefinedAt
Итак, как мы можем проверить, соответствует ли шаблон определенному значению?
Это стало для меня проблемой, чтобы обернуть свой мозг вокруг того, как все делается в Clojure. Как у меня есть функция, которая представляет сопоставление с образцом, и я могу запросить его, чтобы определить, определен ли он в точке, не вызывая вычисления на правой стороне, что может иметь побочные эффекты.
Ответ арности. Scala имеет функции с определенной арностью. Оказывается, что Clojure может иметь единственную функцию, которая ведет себя по-разному в зависимости от арности вызова. Ура!
Итак, макрос выглядит так:
|
01
02
03
04
05
06
07
08
09
10
|
(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)))))) |
Это дает нам функцию, которую можно вызывать с помощью одного параметра:
|
1
|
(pf 44) |
И это может быть вызвано с 2 параметрами:
|
1
|
(pf :defined? 44) |
И я добавил возможность получить тело оригинала, чтобы я мог добавить функцию orElse , которая фактически PartialFunction новую PartialFunction которая является компиляцией составных шаблонов, чтобы шаблоны были скомпилированы более эффективно.
Первый палец в воде
Ага. Я думаю, что Clojure довольно мощный. С помощью макросов я добавил в Clojure одну из самых удивительно мощных языковых функций Scala в несколько строк. Вода чувствует себя довольно хорошо до сих пор.