Created
August 3, 2020 20:19
-
-
Save joinr/2dde3d59115e910ff6c2864fae46725d to your computer and use it in GitHub Desktop.
of types and pizza
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
(ns pizza | |
(:require [clojure.spec.alpha :as s])) | |
(s/def :order/id int?) | |
(s/def :order/status #{:received :delivered :cancelled}) | |
(s/def :pizza/kind #{:plain :pepperoni :hawaiian}) | |
(s/def ::pizza-order (s/keys :req [:order/id :order/status :pizza/kind])) | |
;;pack some "type" into the data | |
(def my-pizza-order | |
{:order/id 1234 | |
:order/type :pizza | |
:order/status :delivered | |
:pizza/kind :hawaiian}) | |
;;define a new type... | |
(defrecord pizza-order [id status kind]) | |
(def my-other-pizza-order (->pizza-order 0 :received :plain)) | |
;;we could just use functions. this may suffice if | |
;;there are a small number of possibilities, or if | |
;;we can abstract out the predicates into something simple. | |
(defn pizza-record? [m] | |
(instance? pizza.pizza-order m )) | |
(defn simple-handle [m] | |
(cond (pizza-record? m) | |
(get m :kind) | |
(and (map? m) | |
(= (get m :order/type) :pizza)) | |
(get m :pizza/kind) | |
:else :no-idea)) | |
;; pizza> (simple-handle my-pizza-order) | |
;; :hawaiian | |
;; pizza> (simple-handle my-other-pizza-order) | |
;; :plain | |
;;define a multimethod to infer "type" from the input | |
;;define different implementations for different "types" of order | |
;;We look up the :order/type key in the input, otherwise | |
;;dispatch on the input's concrete type: | |
(defmulti handle-order | |
(fn [m] | |
(or (get m :order/type) | |
(type m)))) | |
(defmethod handle-order :default [order] | |
(println "no idea!")) | |
(defmethod handle-order nil [order] | |
(println "no-idea!")) | |
(defmethod handle-order :pizza [order] | |
(println (str "Hey, it's a " (order :pizza/kind) " pizza!"))) | |
;; pizza> (handle-order my-pizza-order) | |
;; Hey, it's a :hawaiian pizza! | |
;; nil | |
;; pizza> (handle-order my-other-pizza-order) | |
;; no-idea! | |
;; nil | |
(defmethod handle-order pizza.pizza-order [order] | |
(println (str "Hey, it's a " (get order :kind) " pizza!"))) | |
;; pizza> (handle-order my-other-pizza-order) | |
;; Hey, it's a :plain pizza! | |
;; nil | |
;;define a protocol, which will dispatch on the actual type of | |
;;its argument to determine implementation | |
(defprotocol IOrderHandler | |
(-handle-order [this])) | |
;;define a new type that implements the protocol | |
(defrecord smart-pizza-order [id status kind] | |
IOrderHandler | |
(-handle-order [this] | |
(println (str "Hey, it's a strongly-typed " kind " pizza!")))) | |
(def smarter-pizza-order (->smart-pizza-order 0 :received :pepperoni)) | |
;;extend the protocol to existing concrete types | |
;;implementation just delegates to the multimethod from before. | |
(extend-protocol IOrderHandler | |
clojure.lang.PersistentArrayMap | |
(-handle-order [this] | |
(handle-order this)) | |
clojure.lang.PersistentHashMap | |
(-handle-order [this] | |
(handle-order this)) | |
pizza.pizza-order | |
(-handle-order [this] | |
(handle-order this))) | |
;;pizza> (-handle-order my-other-pizza-order) | |
;;Hey, it's a :plain pizza! | |
;;nil | |
;;pizza> (-handle-order my-pizza-order) | |
;;Hey, it's a :hawaiian pizza! | |
;; pizza> (-handle-order smarter-pizza-order) | |
;; Hey, it's a strongly-typed :pepperoni pizza! | |
;; nil | |
;;records add a wrinkle to the existing spec, since their "keys" are | |
;;not namespace-qualified. | |
(s/def ::pizza-order-unqualified (s/keys :req-un [::id ::status ::kind])) | |
(s/def ::any-pizza-map (s/or :qualified ::pizza-order | |
:unqualified ::pizza-order-unqualified)) | |
;; pizza> (s/valid? ::any-pizza-map my-pizza-order) | |
;; true | |
;; pizza> (s/valid? ::any-pizza-map smarter-pizza-order) | |
;; true | |
;; pizza> (s/valid? ::any-pizza-map my-other-pizza-order) | |
;; true |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment