Sidhant/BG,
Well, I got that far. I ended up with this:
(ns expression-problem.more-geom-shape
(:require [expression_problem.shape])
(:import [expression_problem.shape Shape Square Circle]))
(defprotocol Perimeter
(perimeter [x] "length of shape boundary"))
(extend-protocol Perimeter
Square
Perimeter
(perimeter [this]
(throw
(UnsupportedOperationException. "cannot compute perimeter of Triangle with just breadth and height"))))
But, that tears the Shape abstraction into two- Shape and Perimeter[*]. So, if I get this right if someone gets the original protocol wrong, it stays wrong forever, or at least till the library author fixes it.
I acknowledge the point about being able to extend existing types with new protocols. That is an interesting intersection when comparing to most modern dynamic[**] OO languages.
What I'm getting is that, while Clojure doesn't entirely sidestep the Expression Problem, it makes it more manageable by separating specification and implementation. I guess that's a pretty good design trade-off. I would love any references as to why the Clojure team decided on this approach to understand their specific motivations. BG, do you have any links handy?
Note that I'm specifically not mentioning the namespace scoping because, while a very interesting feature, it is somewhat orthogonal to the Expression Problem.
Thanks
- d
PS- Sidhant, I don't know if you tried running the code you put up, but fu.shape/Square didn't work for me. I figure that's cause types end up in the import hierarchy and not the require hierarchy. Just saying...
* Sidhant, I like the name ClosedShape, but then it makes the area function feel like it's not part of closed shapes, which... is about to make my head explode! :S
** dynamically typed as well as dynamically modifiable