You are here:   ArielOrtiz.com > Programming Languages > Macros

Macros

Objectives

During this activity, students should be able to:

This activity helps the student develop the following skills, values and attitudes: ability to analyze and synthesize, capacity for identifying and solving problems, and efficient use of computer systems.

Activity Description

Individually, solve the following set of meta-programming exercises using Clojure macros.

Place all your functions in a namespace called macros.

  1. This is the documentation for Clojure's or macro:

    or
    macro

    Usage:

    (or)
    (or x)
    (or x & next)

    Evaluates its expressions one at a time, from left to right. If a form returns a logical true value, it returns that value and doesn't evaluate any of the other expressions, otherwise it returns the value of the last expression. (or) returns nil.

    Write a macro called my-or that works as described above. Make sure that any expression gets evaluated at most once. You may not use Clojure's or macro. Usage examples:

    (macros/my-or)                                   ⇒ nil
    (macros/my-or false :one nil :two false :three)  ⇒ :one
    (macros/my-or false false nil)                   ⇒ nil
    (macros/my-or nil nil false)                     ⇒ false
    
  2. Write a macro called do-loop that implements a post-test loop control statement. It must combine the functionality of C's do-while statement and Pascal's repeat-until statement.

    This construct consists of a body (one or more expressions, presumably with side effects) and a final conditional form prefaced with a :while or :until keyword. First, the expressions in the body are evaluated sequentially, and then the condition is evaluated. If the final form uses a :while keyword, the body of the loop is repeated while the condition holds true. On the other hand, if the final form uses an :until keyword, the body of the loop is repeated while the condition holds false (or in other words, the loop terminates when the condition is true). Returns nil.

    Here are some examples:

    (def i (atom 0))
    (macros/do-loop
      (println @i)
      (swap! i inc)
      (:until (= @i 5)))
        ;;; prints:
        ;;; 0
        ;;; 1
        ;;; 2
        ;;; 3
        ;;; 4
        ⇒ nil
        
    (def j (atom 1))
    (macros/do-loop
      (println @j)
      (swap! j inc)
      (:while (<= @j 5)))
        ;;; prints:    
        ;;; 1
        ;;; 2
        ;;; 3
        ;;; 4
        ;;; 5
        ⇒ nil
  3. Write a macro called def-pred, that takes a name, an arg vector, and a body of one or more expressions. The macro should define two predicate functions: a regular one and its negated version. The name of the negated predicate should be the same as name but with a "not-" prefix, and its result should be negated using the not function (see the test code for an example).

    Examples:

    (macroexpand-1 '(macros/def-pred less-than-one? [x] (< x 1)))
      ⇒(do (clojure.core/defn less-than-one? [x] (< x 1)) 
           (clojure.core/defn not-less-than-one? [x] (clojure.core/not (do (< x 1)))))
           
    (macros/def-pred less-than-one? [x] (< x 1))
    
    (less-than-one? 0)
      ⇒ true
      
    (less-than-one? 2)
      ⇒ false
      
    (not-less-than-one? 0)
      ⇒ false
      
    (not-less-than-one? 2)
      ⇒ true
    
    (macroexpand-1 '(macros/def-pred plural? [s] (println "check s in" s) (= \s (last s))))
      ⇒ (do (clojure.core/defn plural? [s] (println "check s in" s) (= \s (last s)))
            (clojure.core/defn not-plural? [s] (clojure.core/not (do (println "check s in" s) 
                                                                     (= \s (last s))))))
    
    (macros/def-pred plural? [s] (println "check s in" s) (= \s (last s)))
    
    (plural? "boys")
      ;;; prints:    
      ;;; check s in boys
      ⇒ true
      
    (plural? "girl")
      ;;; prints:    
      ;;; check s in girl
      ⇒ false
      
    (not-plural? "boys")
      ;;; prints:    
      ;;; check s in boys
      ⇒ false
      
    (not-plural? "girl")
      ;;; prints:    
      ;;; check s in girl
      ⇒ true
    

    NOTE: You will likely need to use the Clojure functions str and symbol when producing the negated predicate name.

  4. Write a macro called defn-curry, that performs a currying transformation to a function definition. It takes as parameters a name, an args vector, and a body of one or more expressions. The macro should define a function called name that takes only the first argument from args and returns a function that takes the second argument from args and returns a function that takes the third argument from args, and so on. The last function returned takes the last argument from args and evaluates all the expressions in body using a do special form (see the test code for examples).

    Usage examples:

    (macroexpand-1 '(macros/defn-curry sum [a b c d] (prn 'args a b c d) (+ a b c d)))
      ⇒ (clojure.core/defn sum [a]
          (clojure.core/fn [b]
            (clojure.core/fn [c]
              (clojure.core/fn [d] (do (prn (quote args) a b c d) (+ a b c d))))))
              
    (macros/defn-curry sum [a b c d] (prn 'args a b c d) (+ a b c d))
    
    ((((sum 1) 2) 3) 4)
      ;;; prints:    
      ;;; args 1 2 3 4
      ⇒ 10
      
    ((((sum 15) 8) 16) 42)
      ;;; prints:    
      ;;; args 15 8 16 42
      ⇒ 81
      
    (macroexpand-1 '(macros/defn-curry go [x y] (* x (+ y 1))))
      ⇒ (clojure.core/defn go [x]
          (clojure.core/fn [y] (do (* x (+ y 1)))))
    
    (macros/defn-curry go [x y] (* x (+ y 1)))
    
    ((go 2) 3) ⇒ 8
      
    ((go 3) 2) ⇒ 9
    
    (macroexpand-1 '(macros/defn-curry add1 [x] (+ x 1)))
      ⇒ (clojure.core/defn add1 [x] (do (+ x 1)))
      
    (macros/defn-curry add1 [x] (+ x 1))
    
    (add1 0) ⇒ 1
    
    (add1 41) ⇒ 42
    
    (macroexpand-1 '(macros/defn-curry hello [] "hello"))
       ⇒ (clojure.core/defn hello [] (do "hello"))
       
    (macros/defn-curry hello [] "hello")
    
    (hello) ⇒ "hello"

Deliverables

Using the Online Assignment Delivery System (SETA), deliver the file called macros.clj containing your solutions. No assignments will be accepted through e-mail or any other means.

IMPORTANT: The program source file must include at the top the author's personal information (name and student id) within comments. For example:

   
;;; ITESM CEM, November 26, 2013.
;;; Clojure Source File
;;; Activity: Macros
;;; Author: Steve Rogers, 1166611

    .
    . (The rest of the program goes here)
    .

Due date is Tuesday, November 26.

Evaluation

This activity will be evaluated using the following criteria:

-10 The program doesn't contain within comments the author's personal information.
10 The program contains syntax errors.
DA The program was plagiarized.
10-100 Depending on the amount of exercises that were solved correctly.