Я начал жесткое кодирование в 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 в несколько строк. Вода чувствует себя довольно хорошо до сих пор.