Skip to content

Instantly share code, notes, and snippets.

@izmailoff
Forked from chrisdone/expression_problem.hs
Created January 2, 2018 08:47
Show Gist options
  • Save izmailoff/e11e1efef5f93d81333bba9fb54b3f57 to your computer and use it in GitHub Desktop.
Save izmailoff/e11e1efef5f93d81333bba9fb54b3f57 to your computer and use it in GitHub Desktop.
Solving the Expression Problem with Haskell
{-# LANGUAGE NamedFieldPuns #-}
-- 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")
data Triangle = Triangle {a::Double,b::Double,c::Double}
data Square = Square {edge::Double}
-- protocols
class Areable shape where
-- calculates the shape's area
area :: shape -> Double
class SelfAware shape where
-- returns the name of the shape
whoami :: shape -> String
-- implementations
instance Areable Triangle where
-- use Heron's formula to calculate area
area Triangle {a, b, c} =
let s = (a + b + c) / 2
in sqrt (s * (s - a) * (s - b) * (s - c))
instance SelfAware Triangle where
whoami _ = "Triangle"
instance Areable Square where
area Square {edge} = edge * edge
instance SelfAware Square where
whoami _ = "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 !
--
class Perimeterable shape where
-- calculates the perimeter of the shape
perimeter :: shape -> Double
instance Perimeterable Triangle where
perimeter Triangle {a,b,c} = a + b + c
instance Perimeterable Square where
perimeter Square {edge} = edge * 4
-- 2.
-- a new shape is discovered: Circle
-- can we add a this new shape without altering existing code ?
-- => yes we can !
--
data Circle = Circle { r :: Double }
instance Areable Circle where
area Circle {r} = pi * r ^ 2
instance SelfAware Circle where
whoami _ = "Circle"
instance Perimeterable Circle where
perimeter Circle {r} = r * 2
-- "tests"
-- > area (Triangle 1 1 1)
-- 0.4330127018922193
-- > whoami (Triangle 1 1 1)
-- "Triangle"
-- > perimeter (Triangle 1 1 1)
-- 3.0
-- > area (Square 2)
-- 4.0
-- > whoami (Square 2)
-- "Square"
-- > perimeter (Square 2)
-- 8.0
-- > area (Circle 3)
-- 28.274333882308138
-- > whoami (Circle 3)
-- "Circle"
-- > perimeter (Circle 3)
-- 6.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment