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"}}}
deps.edn
Extensible Data Notation
{:hello (clojure-version)}
0.1s
Clojure

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
0.1s
Clojure
(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
0.1s
Clojure
;; "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
0.0s
Clojure
;; "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
0.0s
Clojure
;; "Or with many arguments"
(= "Hello to this group: Peter, Paul, Mary!"
    (hello "Peter" "Paul" "Mary"))
;; skill 10004: Invoke/Call a function with multiple arguments.
0.0s
Clojure
;; "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?
0.0s
Clojure
;; "Animals have different names"
(= "Thumper eats veggies."
    (diet {:species "rabbit" :name "Thumper" :age 1 :eater :herbivore}))
0.0s
Clojure
;; "Different methods are used depending on the dispatch function result"
(= "Simba eats animals."
    (diet {:species "lion" :name "Simba" :age 1 :eater :carnivore}))
0.0s
Clojure
;; "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"?
0.0s
Clojure

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})
0.1s
Clojure
(needs-food steve)
0.0s
Clojure
(needs-food joe)
0.0s
Clojure
(defn needs-food-if [person]
  (if (:is-hungry person)
    "This person needs food."
    "This person doesn't need food."))
0.0s
Clojure
(needs-food-if steve)
0.0s
Clojure
(needs-food-if joe)
0.0s
Clojure
;; question 10012: Where do defmulti+defmethod really shine as useful/helpful constructs? 
Clojure
;; question 10013: Please describe this example in plain words: https://clojuredocs.org/clojure.core/defmethod#example-542692c7c026201cdc3269cb
Clojure
Runtimes (1)