Skip to content

Instantly share code, notes, and snippets.

@sritchie
Created January 25, 2012 07:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save sritchie/1675271 to your computer and use it in GitHub Desktop.
Save sritchie/1675271 to your computer and use it in GitHub Desktop.

A little Clojure challenge inspired by Let over Lambda.

Write a macro (you can try a function, but it's impossible) that accepts four arguments:

  1. an expression that returns a number
  2. something to return if that number's negative
  3. something to return if that number's zero
  4. something to return if that number's positive

Here's the signature: (defmacro nif [expr neg zero pos] ...)

To pass my test, the following form should print "pos!" and return "pos.", with no other side effects:

(nif 10
      (do (println "Negative!") "negative.")
      (do (Thread/sleep 1000000) "zero.")
      (do (println "pos!") "pos."))

May the best lisper win!

@bakkdoor
Copy link

Does this count as well? No macros used, I promise :P

class Number {
  def negative: neg zero: zero positive: pos {
    match self {
      case 0 -> zero
      case @{ < 0 } -> neg
      case _ -> pos
    } call
  }
}

# prints "pos!" and returns "pos."
10 negative: {
  "negative!" println; "negative."
} zero: {
  Thread sleep: 1000000; "zero."
} positive: {
  "pos!" println; "pos."
}

@sritchie
Copy link
Author

Nice! FancyLang for the WIN! Are you passing in functions to be called depending on the condition?

@samaaron
Copy link

Fun little puzzle! Thanks :-)

@bakkdoor
Copy link

@sritchie Kind of. They're blocks and allow non-local returns but in this case that doesn't matter. They're similar to regular lambdas. :)

@mdiin
Copy link

mdiin commented Jan 25, 2012

I'm not sure, since you're saying it is impossible without a macro I must have misunderstood something:

(defn nif [exp neg zero pos]
  (let [e (if (instance? clojure.lang.IFn exp) (exp) exp)]
    (cond (> 0 e) (neg)
          (== 0 e) (zero)
          (< 0 e) (pos))))

@travis
Copy link

travis commented Jan 25, 2012

macro'y solution in my fork

@travis
Copy link

travis commented Jan 25, 2012

@mdiin - your solution does:

user> (nif 10
      (do (println "Negative!") "negative.")
      (do (Thread/sleep 1000000) "zero.")
      (do (println "pos!") "pos."))
pos!
Negative!
"pos."

@mdiin
Copy link

mdiin commented Jan 25, 2012

Ah, right. Because the do special form evaluates the expressions - I had missed that and wrapped each of the (do ...) in a function.

@sritchie
Copy link
Author

@mdiin yeah, the trick with the macro here is that a function evaluates all of it's arguments, so you have to scramble a bit to prevent that evaluation. The nif macro lets you think of the inputs as unevaluated lists (or numbers, or whatever they happen to be), and shuffle them into the proper form before they hit the compiler.

@sritchie
Copy link
Author

@samaaron Thanks! The next quiz will be,

Write a series of macros that wraps clojure.core in a badass, composable, music-generating fiesta of code.

@samaaron
Copy link

@sritchie :-) I'll work on a solution...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment