|
|
|
|
|
by eli_gottlieb
5193 days ago
|
|
As I said above, think about a method like map. map :: [a] -> (a -> b) -> [b]
Well, actually, any functor has a map method, by definition! So we'd have to write a type-class: class Functor f where
fmap :: f a -> (a -> b) -> f b
Now how would we represent this as a generic function? method fmap<a,b>(as: Functor<a>,f: a -> b): Functor<b>
And then implement specialized methods for each actual functor? override fmap<a,b>(as: List<a>,f: a -> b): List<b>
Now everything involving functors performs a dynamic dispatch and might lose type information at its call-site, and we now have to deal with Functor<a> being some kind of superclass, a superclass without data slots of its own just to accommodate our need for this "interface".In Scala, rather than doing that, we would just define Functor[A] as a trait: trait Functor[A] self => {
def map[B](f: A => B): self[A]
}
And now every time I call the map method on a Scala object, it statically dispatches via dot-notation. So my compiler knows that calling map on a List[A] gives me back a List[B], and that calling map on an Option[A] gives me back an Option[B], and I didn't need any implicits to get it done. |
|
http://www.cs.washington.edu/research/projects/cecil/www/cec...