Skip to content

Instantly share code, notes, and snippets.

@optevo
Created January 16, 2014 02:54
Show Gist options
  • Save optevo/8449076 to your computer and use it in GitHub Desktop.
Save optevo/8449076 to your computer and use it in GitHub Desktop.
;Starting with the example of
(def rows 3)
(def cols 3)
(def cells (for [row (range rows), col (range cols)] [row col]))
(defn select-cells-by-row [row]
(filter (fn [[r c]] (= row r)) cells))
(defn print-cells-by-row [row]
(doseq [cell (select-cells-by-row row)]
(println cell)))
;I want to parameterise this and I'd like something that works like a class/object in an OO language
(defn grid [rows cols]
(let [cells (for [row (range rows), col (range cols)] [row col])]
(defn select-cells-by-row [row]
(filter (fn [[r c]] (= row r)) cells))
(defn print-cells-by-row [row]
(doseq [cell (select-cells-by-row row)]
(println cell)))
)
{:print-cells-by-row print-cells-by-row :select-cells-by-row select-cells-by-row}
)
;So I wrap the functions that I already have and make them closures. I could return just the closure I'm interested in,
;but in this case, I'll return all of them. The wrapping function in this way works like a class and a constructor.
;The returned map represents the method instances of that class. I can use my "class" like this:
(def grid-5x5 (grid 5 5))
((grid-5x5 :print-cells-by-row) 2)
;This is analogous to class-instance.method-name(parameter list) in OO
;I can create other grids and call them and they can co-exist without any var rebinding
;Next, let's imagine a macro "make-instance" that does the wrapping defn and also creates a map of closure name
;to closure so I can omit the return value in my example above:
(make-instance grid [rows cols]
(let [cells (for [row (range rows), col (range cols)] [row col])]
(defn select-cells-by-row [row]
(filter (fn [[r c]] (= row r)) cells))
(defn print-cells-by-row [row]
(doseq [cell (select-cells-by-row row)]
(println cell)))
)
)
;I can then use this in the same way as above
(def grid-5x5 (grid 5 5))
;I could then make another macro "use-instance" which will replace a symbol wherever it matches a keyword in the closure map.
;For example, this
(use-instance grid-5x5
(select-cells-by-row 2)
(print-cells-by-row 3)
)
;would expand to
((grid-5x5 :select-cells-by-row) 2)
((grid-5x5 :print-cells-by-row) 3)
;These macros would be useful if I have a lot of function definitions that refer to vars that I now want to parameterise
;by using make-instance. I can also then parameterise the calls to any existing functions without changing any code other
;than wrapping it in "use-instance". I can the make/use more instances of these parameterised functions as needed.
;There are other ways you could write macros to bind the closures created but - this is just one way it
;could be done.
@Engelberg
Copy link

This an interesting trick. I like the idea.

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