Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@vpatryshev
Last active December 14, 2015 08:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vpatryshev/5057806 to your computer and use it in GitHub Desktop.
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 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