2. Healthy Code
January - 2015
15
Jugalbandi inspires and fuels our desire to learn by focusing
on dialogue and exploration. More languages in the room
meant more perspectives too. Exploring a paradigm through
any single language could. Above all, Code Jugalbandi is a
Creator and Listener
We met regularly over a period of several months, exploring
some key issues related to Functional Programming
(FP). This culminated in a live Code Jugalbandi at the
Functional Conference in Bengaluru, 2014. During the
FP Code Jugalbandi, we explored nine themes, which
became melodies. Similar to the musical Jugalbandi, there
are two roles: creator and listener. We called the creator of
the melody Bramha and the one who listens and responds
Krishna
explore one such melody: The Expression Problem.
The Expression Problem
The Expression Problem was coined by Philip Wadler.
Wikipedia states that the goal of the Expression Problem
is to
cases to the datatype and new functions over the datatype,
without recompiling existing code, and while retaining
static type safety (e.g. no casts). According to Wadler,
“Whether a language can solve the Expression Problem is a
salient indicator of its capacity for expression.”
The Expression Problem - Clojure
BRAMHA: In OOP, we model our domain concepts by
creating our own types i.e., using classes and interfaces. In
a geometric shape, say the Circle, in Clojure.
(defrecord Circle [radius])
(Circle. 11)
(:radius (Circle. 11))
BRAMHA: Classes in OO act as containers for structured
data, as well as behaviour acting on the structured data. In
Clojure, records just take care of the structure.
BRAMHA: In Clojure, the corollary of an Interface is
a Protocol:
(defprotocol Shape
(area [shape])
(perimeter [shape]))
BRAMHA: We can extend our Circle type to implement
the Shape protocol, e.g.
(extend-type Circle
Shape
(area [circle]
(* (:r circle) (:r circle) Math/PI)))
BRAMHA: At this stage we have a simple type-based
dispatch for our protocol method area. Clojure is more
powerful but less performant polymorphic dispatch
through multimethods.
(area (Circle. 10))
(area (Rect. 10 20))
BRAMHA
something like this: (EP1) create a new type that implements
an existing interface
BRAMHA
our existing Shape protocol:
(defrecord Rect [length width]
Shape
(area [rect]
(* (:length rect) (:width rect))))
KRISHNA
allow you to create new classes to implement existing
BRAMHA: Yes, agreed, but the second part of the Expression
Problem is harder to solve in some OO languages:
(EP2) implement a new interface for an existing type
Moreover (EP1) and (EP2) should be possible without having
in working with third party code that you cannot recompile.
To achieve (EP2) with Clojure we can do this:
(defprotocol Graphic
(draw [this]))
(extend-type Circle
Graphic
(draw [this] (puts “O”)))
KRISHNA: Clojure fares well with both (EP1) and (EP2). Let
me show you how Scala addresses the Expression Problem.
The Expression Problem - Scala
KRISHNA: We start by creating the Circle and Rectangle as
data classes.
3. Healthy Code
January - 2015
16
case class Circle(r: Int)
case class Rectangle(l: Int, w: Int)
KRISHNA Shape
that I can perform on Circle and Rectangle.
trait Shape[T] {
def area(t: T): Double
def perimeter(t: T): Double
}
Then I implement the trait for Circle, say,
implicit object CircleOps extends Shape[Circle] {
def area(c: Circle): Double = Math.PI * c.r * c.r
def perimeter(c: Circle): Double = 2 * Math.PI
* c.r }
KRISHNA
def area[T](t: T)(implicit s: Shape[T]): Double =
s.area(t)
def perimeter[T](t: T)(implicit s: Shape[T]):
Double =
s.perimeter(t)
KRISHNA: Returning to the Expression Problem, we can
solve (EP2) like this in Scala:
trait Graphics[T] {
def draw(t: T): Unit
}
implicit object CircleGraphics extends
Graphics[Circle] {
def draw(c: Circle) = println(“O”)
}
def draw[T](t: T)(implicit g: Graphics[T]) =
g.draw(t)
KRISHNA
a new Right Triangle type and implement the existing
interfaces.
case class RTriangle(b: Int, h: Int)
import Math._
implicit object RTriangleOps extends Shape[RTriangle]
with Graphics[RTriangle] {
def area(rt: RTriangle) = 0.5 * rt.b * rt.h
def perimeter(rt: RTriangle) =
rt.b + rt.h + (sqrt(pow(rt.b, 2) +
pow(rt.h, 2))
def draw(rt: RTriangle) = println(rt)
}
BRAHMA: Scala solves both (EP1) and (EP2), albeit with
some syntactic noise around the use of implicit objects.
Let me show you how concise Haskell is at solving the
Expression Problem.
The Expression Problem - Haskell
BRAMHA
with a few sub-types:
data Shape = Circle {radius :: Float}
| Rect {length :: Float, width ::Float}
deriving Show
BRAMHA: To create an area method that is polymorphic
over the Shape sub-types:
area :: Shape -> Float
area (Circle r) = 3.14 * r^2
area (Rect l w) = l * w
BRAMHA
In (EP1) adding a new sub-type to the Shape algebraic type
would require adding a clause for the new subtype to each
function I declared over the type (implying a recompile),
In (EP2) adding a new function acting on existing algebraic
types is trivial.
The Expression Problem with TypeClasses
Instead of unifying the concepts Circle and Rect as the Shape
abstract data type, we could use Haskell typeclasses to unify
to not name clash with the above code).
data Circle_ = Circle_ {radius_ :: Float}
data Rect_ = Rect_ {length_ :: Float, width_ ::
Float}
class Shape_ a where
area_ :: a -> Float
perimeter_ :: a -> Float
4. Healthy Code
January - 2015
17
BRAMHA: Adding a new data type that implements an
existing typeclass is easy (EP1):
instance Shape_ Circle_ where
area_ (Circle_ r) = pi * r^2
perimeter_(Circle_ r) = 2 * pi * r
Extending an existing type with a new interface is simple
too (EP2):
class Graphics_ a where
draw_ :: a -> IO ()
instance Graphics_ Circle_ where
draw_ (Circle_ r) = putStrLn (show r)
KRISHNA: In OOP classes, we can combine type with
a clear separation between type and behaviour.
BRAMHA
is that objects contain mutable state. In FP languages, we use
immutable values in place of mutable state. For example,
the Circle and Rect in all three languages were some form of
KRISHNA: The Expression Problem also asks that you can
extend types with interfaces without needing to recompile
the code you are extending. Our three languages are quite
BRAMHA: Scala is more strictly typed, but also fares well
by allowing extension of code without recompiling it. If we
look, both the Scala and Clojure both resemble Haskell in the
KRISHNA: Yes, and Haskell deals with types in a
sophisticated yet simple way, but it is less dynamic and more
limited in solving this aspect of the (EP).
BRAMHA
KRISHNA
than traditional OO does, but then again, many OO
languages have evolved and are much more dynamic these
days. In fact, using Java Generics, the Expression Problem
can be solved, except that it would be syntactically very noisy
Ryan is a software developer,
coach and advisor, based in Cape
Town. He has been working with
code for more than 15 years. Ryan
assists individuals and teams
to manage the evolution and
complexity of their software
systems. Ryan is a passionate
learner and enjoys facilitating
learning in others.
Dhavalahands-ondeveloperand
mentor, believes that software
then a craft. For him writing
software brings his creativity
to the fore. His interests are in
architecting applications ground
up, estabilishing environments,
transitioning and orienting
teams to Agile way of working
by embedding himself within
teams.
CodeJugalbandi
object
BRAHMA: True, and besides, there are very few purely FP
or OO languages. Still, we can say that FP does have good
answers to the (EP).
References
Learn more from