Wishful Coding

Didn't you ever wish your
computer understood you?

Can't take value of a macro

Do. not. ever. make me see that exception. Because in my opinion that means you failed at designing your shiny DSL.

A macro is not a thing, but a transform from one thing to another. If your DSL does not have things – domain specific values, as Christophe Grand calls them – at its core, you’re doing it wrong.

First and foremost, a macro should be simple, where simple means ‘not compound’, as explained by Stuart Halloway.

I would like to define a simple macro as one that is written as a compound of simple functions returning code that evaluates to a simple or composite value.

  • (+ 1 1) returns a simple value
  • (a-record. 1 2 3) evaluates to a composite of simple things
  • (do (def foo 2) (def bar 4) (do-thing-to foo)) is not simple

Another good non-reason to use macros is to defer execution.

(defmacro assoc-once [m k v]
  `(if-not (get ~m ~k)
     (assoc ~m ~k ~v)
     ~m))

(assoc-once {:a 1} :a (println "foo"))
; {:a 1}
(assoc-once {:a 1} :b (println "foo"))
; foo
; {:b nil, :a 1}

Works nicely, uh? Now assume we have an atom, try (swap! a assoc-once ...) It’ll honor our post title: Can’t take value of a macro

You know what else defers execution? A function.

(defn assoc-once [m k v]
  (if-not (get m k)
    (assoc m k (v)) ; note parens
    m))

(assoc-once {:a 1} :a #(println "foo"))
; {:a 1}
(assoc-once {:a 1} :b #(println "foo"))
; foo
; {:b nil, :a 1}

This is of course not as good-looking as the macro, but the swap! case works fine here. If you care enough, you could define a macro on top of the function, giving you a simple macro.

(defmacro assoc-once [m k v]
  `(assoc-once* ~m ~k (fn [] ~v)))

Now, this is a very long and opinionated post for me already, so I’m going to stop here. I highly recommend that you watch the 2 video’s I linked to.

I had in mind to take you through another example of using protocols and function composition to simplify and avoid macros, but I’ll just give you this, this and that as a homework assignment.

Published on

Generative Music

It’s been a long time since I played with Overtone. What I’ve always wanted to do is generate music automatically. Today I ran into jFugue, which makes this really easy, listen to this:

</param> </param> </embed>

Markov generated music by pepijndevos

I just took some music from wikifonia, constructed a Markov-chain and did a random note-walk.

This could work just as well with Overtone, but I used jFugue because it comes with a parser for MusicXML files.

To run the code below, you need to have jFugue on the classpath. Then just pipe a couple of notes from generate into play.

(ns ponger.core
  (:require clojure.string))

(defn inflate-mxl [file]
  (let [z (java.util.zip.ZipFile. file)]
    (slurp
      (.getInputStream z
        (.nextElement (.entries z))))))

(defn parse-musicxml [file]
  (let [parser   (org.jfugue.parsers.MusicXmlParser.)
        renderer (org.jfugue.MusicStringRenderer.)]
    (.addParserListener parser renderer)
    (.parse parser (inflate-mxl file))
    (.getPattern renderer)))

(defn notes [tokens]
  (filter #(re-matches #"[A-GR].*" %) tokens))

(defn voices [tokens]
  (let [[f [_ & r]] (split-with (complement #(.startsWith % "V")) tokens)]
    (when (seq f)
      (cons f (voices r)))))

(defn make-chain [tokens]
  (into {}
    (for [[k slice] (group-by first (partition 2 1 tokens))]
      [k (map last slice)])))

(defn walk-chain [chain start]
  (let [nxt (rand-nth
              (get chain start
                   (apply concat (vals chain))))]
    (cons
      nxt
      (lazy-seq
        (walk-chain chain nxt)))))

(defn generate [path]
  (-> path
    parse-musicxml
    (.getTokens)
    notes
    make-chain
    (walk-chain nil)))

(defn play [token-seq]
  (let [player (org.jfugue.StreamingPlayer.)]
    (doseq [token token-seq]
      (.stream player token))))

(defn save [token-seq path]
  (.saveMidi (org.jfugue.Player.)
         (clojure.string/join " " token-seq)
         (java.io.File. path)))

NIO in Clojure

So, I’m playing with the idea of writing a little webserver in Clojure, much like those asynchronous Python ones like Twisted and Tornado, but actually I’m just writing this to play with my new blog.

There you have the code, not as a gist this time. The code actually lives in a separate file on Github, and is included here by a few lines of Liquid markup.

This code was modeled after this Java code. If you have anything interesting to say about this code, NIO, webservers or Clojure, I’d love to hear from you.

(ns nio
  (:import
     [java.net
      InetSocketAddress]
     [java.nio
      ByteBuffer
      CharBuffer]
     [java.nio.channels
      ServerSocketChannel
      Selector
      SelectionKey]
     [java.nio.charset
      Charset]))

(def *buffer* (ByteBuffer/allocate 16384))

(defn selector [server-socket-channel]
  (let [selector (Selector/open)]
    (.register server-socket-channel selector SelectionKey/OP_ACCEPT)
    selector))

(defn setup [port]
  (let [server-socket-channel (ServerSocketChannel/open)
        _ (.configureBlocking server-socket-channel false)
        server-socket (.socket server-socket-channel)
        inet-socket-address (InetSocketAddress. port)]
    (.bind server-socket inet-socket-address)
    [(selector server-socket-channel)
     server-socket]))

(defn state= [state channel]
  (= (bit-and (.readyOps channel) state) state))

(defn buffer->string
  ([byte-buffer]
   (buffer->string byte-buffer (Charset/defaultCharset)))
  ([byte-buffer charset]
   (.toString (.decode charset byte-buffer))))

(defn string->buffer
  ([string]
   (string->buffer string (Charset/defaultCharset)))
  ([string charset]
   (.encode charset string)))

(defn accept-connection [server-socket selector]
  (let [channel (-> server-socket (.accept) (.getChannel))]
    (println "Connection from" channel)
    (doto channel
      (.configureBlocking false)
      (.register selector SelectionKey/OP_READ))))

(defn read-socket [selected-key]
  (let [socket-channel (.channel selected-key)]
    (.clear *buffer*)
    (.read socket-channel *buffer*)
    (.flip *buffer*)
    (if (= (.limit *buffer*) 0)
      (do
        (println "Lost connection from" socket-channel)
        (.cancel selected-key)
        (.close (.socket socket-channel)))
      (.write socket-channel *buffer*))))

(defn react [selector server-socket]
  (while true
    (when (> (.select selector) 0)
      (let [selected-keys (.selectedKeys selector)]
        (doseq [k selected-keys]
          (condp state= k
            SelectionKey/OP_ACCEPT
            (accept-connection server-socket selector)
            SelectionKey/OP_READ
            (read-socket k)))
        (.clear selected-keys)))))

(defn run []
  (apply react (setup 2323)))
Published on