Skip to content

Instantly share code, notes, and snippets.

@elnygren
Last active December 3, 2022 16:33
Show Gist options
  • Save elnygren/e34368a86d62f0cb75f04ba903f7834a to your computer and use it in GitHub Desktop.
Save elnygren/e34368a86d62f0cb75f04ba903f7834a to your computer and use it in GitHub Desktop.
Solving the Expression Problem with Clojure
; The Expression Problem and my sources:
; http://stackoverflow.com/questions/3596366/what-is-the-expression-problem
; http://blog.ontoillogical.com/blog/2014/10/18/solving-the-expression-problem-in-clojure/
; http://eli.thegreenplace.net/2016/the-expression-problem-and-its-solutions/
; http://www.ibm.com/developerworks/library/j-clojure-protocols/
; To begin demonstrating the problem, we first need some
; "legacy code" with datastructures and functionality:
; data structures ("shapes")
(defrecord Triangle [a b c])
(defrecord Square [edge])
; protocols
(defprotocol Areable
(area [shape] "calculates the shape's area"))
(defprotocol SelfAware
(whoami [shape] "returns the name of the shape"))
; implementations
(extend-type Triangle
Areable
(area [{:keys [a b c]}]
"use Heron's formula to calculate area"
(let [s (/ (+ a b c) 2)]
(Math/sqrt (* s (- s a) (- s b) (- s c)))))
SelfAware
(whoami [this] "Triangle"))
(extend-type Square
Areable
(area [this] (* (:edge this) (:edge this)))
SelfAware
(whoami [this] "Square"))
; Solving the Expression Problem
; 1. adding new functionality
; 2. adding new datastructures (that play well with existing functionality)
; 1.
; a new protocol is invented: Perimeterable
; can we add this new functionality without altering existing code ?
; => yes we can !
;
(defprotocol Perimeterable
(perimeter [shape] "calculates the perimeter of the shape"))
(extend-protocol Perimeterable
Triangle
(perimeter [{:keys [a b c]}] (+ a b c))
Square
(perimeter [square] (* (:edge square) 4)))
; 2.
; a new shape is discovered: Circle
; can we add a this new shape without altering existing code ?
; => yes we can !
;
(defrecord Circle [r]
Areable
(area [this] (* Math/PI (Math/pow (:r this) 2)))
SelfAware
(whoami [this] "Circle")
Perimeterable
(perimeter [this] (* 2 (:r this))))
; "tests"
(area (->Triangle 1 1 1))
(whoami (->Triangle 1 1 1))
(perimeter (->Triangle 1 1 1))
(area (->Square 2))
(whoami (->Square 2))
(perimeter (->Square 2))
(area (->Circle 3))
(whoami (->Circle 3))
(perimeter (->Circle 3))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment