Last active
December 14, 2015 08:29
-
-
Save vpatryshev/5057806 to your computer and use it in GitHub Desktop.
an example how java.util.ArrayList can be turned into a functor and get a functor's map() method.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* This trait (typeclass) defines one operation for a parametrized type F | |
* the operation is known as fmap in Haskell, f1 in internal categories. | |
* | |
* For each pair of arbitrary types X, Y and an arbitrary function:X=>Y, | |
* it should provide a function F[X]=>F[Y]. | |
* Providing means that the implementation depends on a specific type F | |
* E.g. given F = List, and a function f(n: Int): String = "{{" + n + "}}", | |
* we should have a function List[Int]=>List[String] that wraps elements in double braces. | |
* | |
* Note that the function is supposed to be total, not throw any exceptions, or else | |
* things are more complicated, since we'll wind up in a Kleisli category for Exception monad; | |
* implementations should deal with this accordingly, which may be challenging in practice. | |
*/ | |
trait Functor[F[_]] { | |
def map[X,Y](f: X => Y): F[X] => F[Y] | |
} | |
/** | |
* This implicit function takes an instance of type F and returns a new object which | |
* does have method map - so that we can use it. | |
* How does it do it? Where is the implementation of this new map method? | |
* The trick is, it takes implementation from an object of type Functor[F], | |
* if such object can be found by the compiler. | |
* | |
* The phrase 'F[_]: Functor' means that we tell the compiler it should find such an object. | |
* If such an object is found, we can say that F can be turned into a functor (by providing `map`. | |
* | |
* Since the method is implicit, any instance of F[A] placed in a context where `map` is required, | |
* will be converted using this method. | |
* | |
* @param fa an instance of type F[A], for some type A | |
* @tparam F parameterized type that is being augmented with `map` operation | |
* @tparam A any type for which fa is of type F[A] | |
* @return an instance which does contain method map required by Functor typeclass | |
*/ | |
implicit def fops[F[_]: Functor, A](fa: F[A]) = new { | |
val functor = implicitly[Functor[F]] | |
final def map[B](f:A=>B):F[B] = functor.map(f)(fa) | |
} | |
/** | |
* Now an example, let's see if we can prove that ArrayList can be a functor. | |
*/ | |
import java.util._ | |
/** | |
* This is the object that `fops` above can be used, for the case when F==ArrayList, | |
* as a witness of ArrayList's functoriality. | |
* That's all we need it for. We never call its function `map` directly, only through fops. | |
*/ | |
implicit object ArrayList_is_a_Functor extends Functor[ArrayList] { | |
/** | |
* Define `map` for the case when F==ArrayList, according to Functor trait above. | |
* The implementation is trivial, it's just a demo. | |
* | |
* @param f any function from X to Y | |
* @tparam X an arbitrary type | |
* @tparam Y another arbitrary type | |
* @return a function ArrayList[X] => ArrayList[Y] | |
*/ | |
def map[X,Y](f: X => Y) = (listX: ArrayList[X]) => { | |
val listY = new ArrayList[Y] | |
for (i <- 0 until listX.size) listY.add(f(listX.get(i))) | |
listY | |
} | |
} | |
// Now take, for example, an ArrayList of strings; it's all plain old Java | |
val testList = new ArrayList[String](Arrays.asList("this", "is", "a", "test")) | |
// Lo and behold, we can call `map` method on an ArrayList. | |
// This is the power of scala typeclasses: | |
val transformed = testList.map(_.toUpperCase) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment