`loop` и` with-redefs` не очень хорошо играют вместе - PullRequest
0 голосов
/ 12 ноября 2018

У меня есть код, который я реорганизовал только для того, чтобы узнать, что что-то было сломано с loop.После некоторой отладки я обнаружил, что loop и with-redefs не очень хорошо играют вместе.Я понимаю, что может не иметь смысла использовать with-redefs внутри цикла, но я не ожидал, что это не сработает.Я не уверен, намеренно или нет.

Это MCVE, который я создал, чтобы продемонстрировать «проблему»:

(loop [test 3]
  (with-redefs []
    (if (zero? test)
      "done"
      (recur (dec test)))))

Это дает мне:

Несоответствующий счетчик аргументов для повторения, ожидаемый: 0 аргументов, полученный: 1

Удаление with-redefs работает как ожидалось:

(loop [test 3]
  (if (zero? test)
    "done"
    (recur (dec test))))

и возвращает "done".

По какой причине первый фрагмент кода не работает?Это намеренно?

1 Ответ

0 голосов
/ 12 ноября 2018

Объяснение заключается в макроразложении with-redefs:

(macroexpand-1
 '(with-redefs []
    (if (zero? test)
      "done"
      (recur (dec test)))))

возвращает:

(with-redefs-fn {}
  (fn []
    (if (zero? test)
      "done"
      (recur (dec test)))))    

, где вы можете видеть, что, поскольку был введен новый fn,recur будет ссылаться на этот fn, а не на дальний loop (что объясняет исключение арности).

Существует множество других макросов, которые "несовместимы" с loop таким образом, поскольку recur должен находиться в хвостовой позиции по отношению к loop, и если recur происходит внутри вызова макроса, макрос может манипулировать кодом так, что recurбольше не в хвостовом положении.

В частности, with-redefs (и ряде других ситуаций), обходной путь может быть:

(loop [test 3]
  (let [[recur? val]
        (with-redefs []
          (if (zero? test)
            [false "done"]
            [true (dec test)]))]
    (if recur?
      (recur val)
      val)))
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...