I am working through the Joy of Clojure book and am now on the multi-methods section. In that book they give an example that is supposed to return one thing, but returns another for me (I have tried both LightTable and Emacs). The code is a little long; I trimmed it down where I could, but apologize for this. It is the second to last command that is not returning as expected (shown inline). How can I make this work, properly?
The helper functions are rather incidental to the problem, so you can skip to the multi-methods first. I included them just in case they were a possible source of the problem. I will add a note at the end explaining them, if you need to know more.
(ns joy.udp
(:refer-clojure :exclude [get]))
;; helpers
(defn beget [this proto]
(assoc this ::prototype proto))
(def clone (partial beget {}))
(defn get [m k]
(when m
(if-let [[_ v] (find m k)]
v
(recur (::prototype m) k))))
;;;;;;; compiler
(defmulti compiler :os)
(defmethod compiler ::unix [m] (get m :c-compiler))
(defmethod compiler ::osx [m] (get m :llvm-compiler))
;;;;;;; home
(defmulti home :os)
(defmethod home ::unix [m] (get m :home))
(defmethod home ::bsd [m] "/home")
;;;;;;; compile-cmd
(defmulti compile-cmd (juxt :os compiler))
(defmethod compile-cmd [:osx "gcc"] [m] (str "/usr/bin/" (get m :c-compiler)))
(defmethod compile-cmd :default [m] (str "Unsure where to locate " (get m :c-compiler)))
;;;;;;;;; hierarchy inheritence
(derive ::osx ::unix)
(derive ::osx ::bsd)
(prefer-method home ::unix ::bsd)
(derive (make-hierarchy) ::osx ::unix)
;;;;;;;;;;;; data-maps
(def unix {:os ::unix, :c-compiler "cc", :home "/home", :dev "/dev"})
(def osx (-> (clone unix)
(assoc :os ::osx)
(assoc :llvm-compiler "clang")
(assoc :home "/Users")))
(compile-cmd osx) ;; This should return "/usr/bin/gcc"
;=> "Unsure where to locate cc"
(compile-cmd unix)
;=> "Unsure where to locate cc"
*Note on helpers: the new get
is redefined in terms of a ns-qualified keyword ::prototype
, that is being used in beget
, which is basically just assoc
-ing a map with that keyword and a another map as its value to the map that is fed into it as an argument. This new {:keyword {map-as-val}} pair, are eventually assoc-ed into the osx data-map, via the clone
function, defined at the bottom. The newly defined data-maps are what serve as the arguments to the multi-method calls at the bottom of the code, above.
I am new to multi-methods and just want to get an intuitive understanding of how polymorphic dispatch works in Clojure. Am I crazy for thinking this is overly complicated?
(defmethod compile-cmd [::osx "gcc"] [m] (str "/usr/bin/" (get m :c-compiler)))
should be
(defmethod compile-cmd [::osx "clang"] [m] (str "/usr/bin/" (get m :c-compiler)))