Здравствуйте, здесь я хотел бы добавить то, что упустил в своем докладе.
Во-первых это “подмена понятий” и вторая функция eval-concat
:
(defn eval-concat [node]
(if (and (= :concat (node-type node))
(every? string? (:args node)))
(string/join (:args node))
node))
Как видно, в ней не указана рекурсия ни в каком виде. Но это уже и не мультиметод.
Может ли там быть мультиметод? Вполне может, ведь входные-выходные значения у них одинаковые: на вход узел, на выходе новый узел. С точки зрения функций они взаимозаменяемы. Вот пример:
(defmulti eval-concat :type)
(defmethod eval-concat :default [node] node)
(defmethod eval-concat :concat [node]
(if (and (= :concat (node-type node))
(every? string? (:args node)))
(string/join (:args node))
node))
Во-вторых, в мультиметоде нельзя указывать более двух функций, по которым будет происходить диспетчеризация. Такая запись недопустима:
> (defmulti foo dispatch-function-1 dispatch-function-2)
> Exception The syntax defmulti has changed
> Example (defmulti name dispatch-fn)
Даже если представить ситуацию, когда мы могли бы вложить две такие функции, то неясно тогда по какому значению выбирать нужную реализацию.
Отвечаю на вопрос Юлии Вячеславовны, можно ли передавать в мультиметод больше, чем один параметр. Ответ: можно, но тогда мы должны потребовать, чтобы функция dispatch-fn
могла их принять и вычислить значение. Например:
(defmulti foo (fn [x y] (+ x y)))
(defmethod foo 100 [x y] (* x y))
> (foo 50 50)
> 2500
Здесь мультиметод может принимать два аргумента, но и dispatch-fn
принимает их тоже. Иначе, если они принимают разное число аргументов, то при любом вызове будет возникать ошибка wrong number of args
либо при вычислении dispatch-fn
, либо уже при вычислении тела мультиметода.
Отдельно хочу сказать спасибо Артему Михайловичу за предложение сделать этот доклад. Надеюсь, что вышло не совсем плохо.