Clojure macros.


(defn myand [arg1 arg2]
  (if arg1 arg2 false))

(myand true false)

(myand false true)

(myand true true)

(and (= 3 5)  (= (/ 1 0) 0))

;(myand (= 3 5) (= (/ 1 0) 0))


;; The trick used in the textbook to implement
;; shortcicuiting and:
(defn myand2 [arg1 arg2]
  (if (arg1) (arg2) false))

(myand2 (fn [] (= 3 5)) (fn [] (= (/ 1 0) 0)))

;; a proper way to do it: macros

;(macroexpand '(and true false))

;; Single quotation mark in an expression:
(+ 2 3)
;; Clojure doesn't evaluate under a quote
;; Lighttable doesn't seem to return quoted lists:
'(+ 1 2)
(seq '(+ 2 3))
'(1 2 3)
(eval '(+ 2 3))


(defmacro compute-infix
  "Takes an infixed form, returns a prefixed form"
  [infixed]
  (list (second infixed) (first infixed) (last infixed)))

(compute-infix (2 + 3))

(compute-infix (3 < 2))

(macroexpand '(compute-infix (2 < 3)))

(defmacro myand-2args [arg1 arg2]
  (list 'if arg1 arg2 false)) ;; note the quote in 'if


(myand-2args  (= 3 5) (= (/ 1 0) 0))

(myand-2args  (= 5 5) (= 4 5))

(myand-2args  (= 5 5) (= 5 5))

(macroexpand '(myand-2args (= 5 5) (= 5 5)))


;; This works, but we want and with multiple args:

(defmacro myand-rec
  "Evaluates exprs one at a time, from left to right. If a form
  returns logical false (nil or false), and returns that value and
  doesn't evaluate any of the other expressions, otherwise it returns
  the value of the last expr. (and) returns true."
  ([] true) ; handling no arguments case
  ([x] x)   ; handling 1-arg case
  ([x & next] ; more than one arg: handled recursively
   `(let [current-val# ~x]  ;; current-val# is a variable generated by gensym
      (if current-val# (myand-rec ~@next) current-val#))))

(myand-rec true (= 5 5) (< 2 5))
(myand-rec true (= 5 5) (< 2 5) (< 5 3))
(myand-rec true (= 5 5) (< 2 5) (< 5 3)  (= (/ 1 0) 0))
(myand-rec (= 5 4))
(myand-rec )

(myand-rec nil)
(myand-rec true 2)

(macroexpand '(myand-rec))
(macroexpand '(myand-rec false))
(macroexpand '(myand-rec (= 4 5) 2))
(macroexpand '(myand-rec (= 4 5) 2 nil))

;; NOTE: seq below is used only to make Lighttable print quoted lists
;; Evaluating in the REPL would work without a seq

;; Elements of a macro:
;; Quoting using `:
`(+ 2 3) ;; should return (clojure.core/+ 2 3)
(seq `(+ 2 3))
`+ ;; shoudl retur clojure.core/+
(list `+)

;; Unquoting using ~
`(+ 1 ~(inc 1)) ;; should return (clojure.core/+ 1 2)
(seq `(+ ~(inc 1) 1))
(seq `(+ (inc 1) 1))

(seq `(+ 1 ~(inc (inc 1))))
(seq `(+ 1 ~(list 1 2 3)))

;; Unquote splicing @ takes a sequence and puts each of its elements directly into the result:
(seq `(+ ~@(list 1 2 3)))
(eval `(+ ~@(list 1 2 3))) ;; note that this doesn't work without the unquote splicing

;; generates a new name every time
(gensym 'hello)
(gensym 'hello)

;; Write a macro 'neither' that takes a various number of arguments,
;; evaluates them while they are false, returns true if they are all
;; false and false if at least one of them is true.

;; (if (neither (= 2 3) (< 5 4)) 1 2) ;; returns 1
;; (if (neither (= 2 3) (< 4 5) (+ (/ 1 0) 0)) 1 2) ;; returns 2

CSci 4651 course web site.