/pset3-lab

Primary LanguageClojureEclipse Public License 1.0EPL-1.0

PSET 3 Lab: Macros!

Due date: November 17.

Problem 1: Unless

Alter the syntax of your language! Implement unless.

Has the same semantics as if, but executes the first branch if the predicate is false and the second branch if the predicate is true.

Problem 2: From

Implement the macro from. The macro binds a symbol and can perform the operations where, orderby, and select over the data it queries.

See the test case under test/pset3/core_test.clj for more details.

Problem 3: With File

Write with-file. It takes a filename, and binds it to the symbol file. Within the macro body, file refers to the java.io.File object.

See the test case under test/pset3/core_test.clj for more details.

Problem 4: Debugging!

What's wrong with this macro?

(defmacro square [n]
  `(* ~n ~n))

Give us a failing case and write a corrected version of the macro!

Problem 5: More debugging

Consider the following numeric if macro, which executes pos, zero, or neg, base on the sign of val.

(defmacro nif [val pos zero neg]
  `(let [~'res ~val]
     (cond (pos? ~'res) ~pos
           (zero? ~'res) ~zero
           :else ~neg)))

The fact below holds true as is:

(nif -1
     "positive"
     "zero"
     "negative") => "negative"

However, this next fact doesn't. Identify the bug and fix it.

 (let [res (java.util.Scanner. (java.io.FileInputStream. "project.clj"))]
   (do (nif 0
            "positive"
            (prn (.nextLine res))
            (prn "negative"))
       (.close res))) => nil

Bonus: Monad comprehension

The construct of monads play a central role in the Haskell programming language, where they underpin the IO system and many other parts of the language. At a high level, a monad allows for composable computation descriptions. For some helpful resources, see

Consider the following list monad:

(def list-monad
  {:return (fn [v] [v])
   :bind (fn [mv f]
           (if (seq mv)
             (apply concat (map f mv))
             []))})

Using the above implementation, the fact below holds true:

(let [bind (:bind list-monad)
      return (:return list-monad)]
  (-> [1 2]
      (bind (fn [a]
              (-> [a (- a)]
                  (bind (fn [b]
                          (return (* 3 b))))))))) => '(3 -3 6 -6)

Now, write a macro, domonad, that permits a nicer monad comprehension syntax as follows:

(domonad list-monad
  [a [1 2]
   b [a, (- a)]]
   (* 3 b)) => '(3 -3 6 -6)

Source: Leonardo Borges's Macro Workshop in Clojure