Clojure Koans 10 Runtime Polymorphism* Notebook
Original Clojure Koans: https://github.com/functional-koans/clojure-koans/
Spoilers Alert
*2021_05_04 Update: According to a Clojure developer friend of mine, there is no "runtime polymorphism" going on in this notebook... So... What exactly is runtime polymorphism, and how can I consistently accurately identify it when I see it? And what is going on here (instead of runtime polymorphism)?
{:deps {org.clojure/clojure {:mvn/version "1.10.1"}
;; complient is used for autocompletion
;; add your libs here (and restart the runtime to pick up changes)
compliment/compliment {:mvn/version "0.3.9"}}}
{:hello (clojure-version)}
Initial Thoughts
I learned about polymorphism formally first in school, in context of Object Oriented Programming ("OOP"). It'll be an interesting, fun, & engaging endeavor for me to pick apart what makes polymorphism without classes or objects.
2021_04_28 Update: With the help of my friends over at Brave Clojurians, I've been fortunate to have some questions answered, and a few new questions raised.
(defn hello
([] "Hello World!")
([a] (str "Hello, you silly " a "."))
([a & more] (str "Hello to this group: "
(apply str
(interpose ", " (cons a more)))
"!")))
;; question 10010: How would a tiny example of defmulti and defmethod look without any multi-arity functions?
;; answer 10010a: See `needs-food` example down below
(defmulti diet (fn [x] (:eater x)))
(defmethod diet :herbivore [a] (str (a :name) " eats veggies."))
(defmethod diet :carnivore [a] (str (a :name) " eats animals."))
(defmethod diet :default [a] (str "I don't know what " (a :name) " eats."))
;; ~~question 10001: What are the similarities and differences between defn, defmulti, defmethod, and multi-arity methods?~~
;; answer 10001a: Marking this as a little too vague and big to answer simply for now.
;; question 10002: Multimethods are useful for what?
;; answer 10002a: Multimethods are useful for, "dispatching on types, values, attributes and metadata of, and relationships between, one or more arguments" reference: https://clojure.org/reference/multimethods
;; answer 10002b: Multimethods are useful for implementing essentially what is conditional logic without needing to write branching code such as if, case, or cond. A simple, concrete example of this is the bunny-lion example code here: https://clojure.org/about/runtime_polymorphism !!!
;; question 10003: What is the purpose/intent/function behind the keyword that follows a function identifier (?) above such as "defmethod diet :herbivore"
;; answer 10003a: This is the "dispatch value."
;; answer 10003b: Clearer answer TBD...
;; question 10004: Might these be examples of "rules" that Rich Hickey mentioned in his talked "Simple Made Easy"?
;; answer 10004a: This question Subjective, and opinionated... Why not ask directly?
;; skill 10001: Define a custom multi-method
;; "Some functions can be used in different ways - with no arguments"
(= "Hello World!" (hello))
;; skill 10002: Invoke/Call a function with no arguments.
;; note: skill 10002 may be duplicated in another notebook, TODO: confirm this is a unique skill
;; "With one argument"
(= "Hello, you silly world." (hello "world"))
;; skill 10003: Invoke/Call a function with one argument.
;; note: skill 10003 may be duplicated in another notebook, TODO: confirm this is a unique skill
;; "Or with many arguments"
(= "Hello to this group: Peter, Paul, Mary!"
(hello "Peter" "Paul" "Mary"))
;; skill 10004: Invoke/Call a function with multiple arguments.
;; "Multimethods allow more complex dispatching"
(= "Bambi eats veggies."
(diet {:species "deer" :name "Bambi" :age 1 :eater :herbivore}))
;; question 10005: What is "dispatching" as refered to above?
;; question 10006: What exactly is a multi-method?
;; "Animals have different names"
(= "Thumper eats veggies."
(diet {:species "rabbit" :name "Thumper" :age 1 :eater :herbivore}))
;; "Different methods are used depending on the dispatch function result"
(= "Simba eats animals."
(diet {:species "lion" :name "Simba" :age 1 :eater :carnivore}))
;; "You may use a default method when no others match"
(= "I don't know what Rich Hickey eats."
(diet {:name "Rich Hickey"}))
;; question 10007: What does runtime polymorphism do, put simply?
;; question 10008: What would be a simple comparison example of runtime polymorphism versus non-runtime polymorphism?
;; question 10009: What are the pieces/elements/parts of "polymorphism a la carte"?
Closing Thoughts
At the risk of sounding like a broken record, I'm struck by a sense of, "There must be much more going on behind the scenes." with Clojure, and in particular today, multimethods. I hope to have more insight into reading stack traces, and understanding how Java is used (for starters) to power (scaffold the functionality of) Clojure.
Room For Improvement
[ ] Compile a list of all skills, questions, descriptions, etc. into one GitHub document or Google Sheets for collaboration
[ ] Compile all suggestions/fixes as issues on single GitHub document
;; question 10011: What would be a super small simple problem be that could be solved by **both** `defmethod`+`defmulti` as well as `if`
;; answer 10011a: See code below.
(defmulti needs-food
(fn [person] (:is-hungry person))) ;; dispatching-function
(defmethod needs-food true [person] "This person needs food.")
;; defmethod identifier-symbol dispatching-value
(defmethod needs-food false [person] "This person doesn't need food.")
(def steve {:is-hungry true})
(def joe {:is-hungry false})
(needs-food steve)
(needs-food joe)
(defn needs-food-if [person]
(if (:is-hungry person)
"This person needs food."
"This person doesn't need food."))
(needs-food-if steve)
(needs-food-if joe)
;; question 10012: Where do defmulti+defmethod really shine as useful/helpful constructs?
;; question 10013: Please describe this example in plain words: https://clojuredocs.org/clojure.core/defmethod#example-542692c7c026201cdc3269cb
[ ] TODO: Parse this page for more skills and questions: https://clojuredocs.org/clojure.core/defmethod
[ ] TODO: Parse and distill syntax composition: https://blog.frankel.ch/learning-clojure/3/
[ ] TODO: Add awesome example "add" function from here: https://stackoverflow.com/questions/12872513/how-and-why-is-defmulti-and-defmethod-used-and-what-are-the-advantages/12872943
[ ] TODO: Create visual (venn diagram perhaps) here for awesome breakdown of difference between method overloading and multimethods: https://cs.stackexchange.com/questions/4660/difference-between-multimethods-and-overloading?newreg=e014ec9730a64db38d4f971ab14e77d9
[ ] TODO: Attempt to linguistically parse this https://clojure.org/reference/multimethods to create a simple-to-complex, comprehensible breakdown & demo
[ ] TODO: Break down examples here: https://stackoverflow.com/questions/28995474/can-the-dispatch-values-of-a-clojure-multimethod-be-queried
[ ] TODO: Analyze and break down: https://stackoverflow.com/questions/28961343/clojure-multimethod-with-multiple-dispatch-values