Created
January 16, 2014 02:54
-
-
Save optevo/8449076 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
;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. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
This an interesting trick. I like the idea.