Given a Haskell Typeclass:
class Point a where
distance :: a -> a -> Double
data Point2D = Point2D { x :: Int
, y :: Int } deriving (Show, Eq)
instance Point Point2D where
distance (Point2D x y) (Point2D x' y') =
sqrt $ ((x - x') ^^ 2) + ((y - y') ^^ 2)
In Haskell we declare a Typeclass constraint and use the function as so.
originDistance :: (Point a) => a -> Foo
originDistance x = distance x (Point2D 0 0)
Scala takes a different approach then Haskell and reifies the dictionary(or if you will vtable), a function with a type class in Haskell actually is de-sugared like below, also making the dictionary explicit:
originDistance_d :: PointDict -> a -> Foo
originDistance_d pd x = pd("distance") x (Point2D 0 0)
In the translated version we explicitly pass the dictionary which is used to dynamically lookup the correct implementation to call. You can see the original formualtions in "How to make ad-hoc polymorphism less ad hoc", Wadler & Blott 1989 or "Type Classes in Haskell", Cordelia V. Hall et. al. 1996.
Scala takes that same approach to provide more flexibility in defining and using these constructs, it achieves this using the already existing implicits.
/* declare the *class* of functionality we want, and make it parametric with one type variable A */
trait Point[A] {
def distance(x: A, y: A): Double
}
We again define a simple 2DPoint class, similar to the Haskell one. We use an implicit value to build a dictionary, or with an implicit object.
case class CartesianCoord(x: Int, y: Int)
implicit val cartesianPoint = new Point[CartesianCoord] {
def distance(x: CartesianCoord, y: CartesianCoord): Double =
Math.sqrt(Math.pow(x.x - y.x, 2) + Math.pow(y.x - y.y, 2))
}
implicit object cartesianPoint extends Point[CartsianCoord] {
def distance(x: CartesianCoord, y: CartesianCoord): Double =
Math.sqrt(Math.pow(x.x - y.x, 2) + Math.pow(y.x - y.y, 2))
}
Implicit's function on type's if you notice both val
and object
define an entity with type Point[CartesianCoord]
.
In scala we can use the type class like below:
def originDistance[A : Point](x: A) = {
val point = implicitly[Point[A]]
point.distance(x, CartesianCoord(0, 0))
}
def foo[A](x: A)(implicit point: Point[A]) =
point.distance(x, CartesianCoord(0, 0))