Clojure, can macros do something that cannot be done with a function

I study Clojure macros and wonder why we cannot only use functions for metaprogramming.

As far as I know, the difference between a macro and a function is that the macro arguments are not evaluated, but passed as data structures and characters as they are, while the return value is calculated (at the place where the macro is called). The macro acts as a proxy between the reader and the evaluator, transforming the form arbitrarily before the assessment. Inside, they can use all language functions, including functions, special forms, literals, recursion, other macros, etc.

The functions are the opposite. Arguments are evaluated before the call, the return value is not returned. But the mirror-like nature of macros and functions makes me wonder if we could also use functions as macros, quoting their arguments (form), transforming the form, evaluating it inside the function, and finally returning its value. Wouldn't that lead to a logical result? Of course, this would be inconvenient, but theoretically, is there an equivalent function for every possible macro?

Here is a simple infix macro

(defmacro infix
  "translate infix notation to clojure form"
  [form]
  (list (second form) (first form) (last form)))

(infix (6 + 6)) ;-> 12

Here is the same logic with the function

(defn infix-fn
  "infix using a function"
  [form]
  ((eval (second form)) (eval (first form)) (eval (last form))))

(infix-fn '(6 + 6)) ;-> 12

Now, is this perception generalizable to all situations, or are there some angular cases where a macro cannot be surpassed? After all, are macros just syntactic sugar over a function call?

+6
1

, , .

infix , :

(let [m 3, n 22] (infix-fn '(m + n)))
CompilerException java.lang.RuntimeException: 
Unable to resolve symbol: m in this context ...

, @jkinski: eval m.


?

. , , , .

  • ;
  • ;
  • ;

.

( Clojure by Halloway and Bedra)

(defmacro unless [test then]
  (list 'if (list 'not test) then)))

... if-not.

(defn safe-div [num denom]
  (unless (zero? denom) (/ num denom)))

... , nil:

(safe-div 10 0)
=> nil

:

(defn unless [test then]
  (if (not test) then))

... then

(safe-div 10 0)
ArithmeticException Divide by zero ...

then unless, unless .

, Clojure case. :

(defmacro my-case [expr & stuff]
  (let [thunk (fn [form] `(fn [] ~form))
        pairs (partition 2 stuff)
        default (if (-> stuff count odd?)
                  (-> stuff last thunk)
                  '(constantly nil))
        [ks vs] (apply map list pairs)
        the-map (zipmap ks (map thunk vs))]
    (list (list the-map expr default))))

  • (ks) (vs),
  • fn,
  • ,
  • , , .

. , .

Python, . , Python case. Rich case, , .


, , if. , , , . if .

my-case:

(defmacro if-like
  ([test then] `(if-like ~test ~then nil))
  ([test then else]
   `(my-case ~test
     false ~else
     nil ~else
     ~then)))

, recur, . ...

(defn fact [n]
  (if-like (pos? n)
    (* (fact (dec n)) n)
    1))

(map fact (range 10))
=> (1 1 2 6 24 120 720 5040 40320 362880)

... , .


, , - .

+11

Source: https://habr.com/ru/post/1017256/


All Articles