Welcome! Please see the About page for a little more info on how this works.

0 votes
in Macros by
;;gets a list like (1 + (1 + 2))
;;returns (+ 1 (+ 1 2))
(defmacro infix [expression]
  (letfn [(helper [expression]
               (cond (not (list? expression)) expression
                     (= (count expression) 0) '()
                     (= (count expression) 1)  (recur (first expression))
                     (not (function? (first expression))) (let [a (first expression)
                                                                op (second expression)
                                                                b (nth expression 2)]
                                                                  (recur (cons (list op a b)
                                                                               (drop 3 expression))))
                    :else expression))]
  (helper expression)))

(macroexpand '(infix (1 + 1 * 2 * 4))) ;; => ((+ 1 1) * 2 * 4)
(macroexpand '(infix (((1))))) ;; => 1

The second recur seems to not work (in (not (function? ....)). What is wrong with the code?

1 Answer

+3 votes
selected by
Best answer

I think there are probably several predicate related issues here. For the first condition, I think you'll want to use something broader than list? (which is just for list collections) and instead maybe use something that includes seqs like sequential?. I'm not sure what function? is, but in your case those will be symbols so symbol? should work. I suspect that's your current issue because that's the branch you're not seeing. You might want to add a fallthrough :else branch in the cond to tell you when it doesn't match.

edited by
Thank you. The function sequential? resolved the case, but why ordinary list check did not?
After debugging I saw that expression (list? '((+ 1 1) * 2 * 4)) somehow returned false.
The doc for `list?` states: Returns true if x implements IPersistentList

Are you certain that exact expression returned false, or are you showing an illustrative example? The result of `list?` depends on how that form was constructed. For example:

(list? (cons '(+ 1 1) '(* 2 * 4))) => false
(list? (list '(+ 1 1) * 2 * 4)) => true

The reason for the first is that the type returned by `cons` is a `clojure.lang.Cons` type.
You are correct, thank you! I forgot that Clojure does not have cons cells.