Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala -- How to use Functors on non-Function types?

While reading the description of Functors on this blog:

https://hseeberger.wordpress.com/2010/11/25/introduction-to-category-theory-in-scala/

there is a generic definition of Functor and a more specific one:

trait GenericFunctor[->>[_, _], ->>>[_, _], F[_]] {
  def fmap[A, B](f: A ->> B): F[A] ->>> F[B]
}
trait Functor[F[_]] extends GenericFunctor[Function, Function, F] {
  final def fmap[A, B](as: F[A])(f: A => B): F[B] =
    fmap(f)(as)
}

Clearly this means Functors can be used with other higher-kinded types besides Function objects. Could someone please give an example or explain how or why or in what scenario that would be done? Namely, what would another implementation of GenericFunctor be in Scala -- that uses a different type constructor from Function? Thanks!

EDIT:

Just to clarify:

object Functor {

  def fmap[A, B, F[_]](as: F[A])(f: A => B)(implicit functor: Functor[F]): F[B] =
    functor.fmap(as)(f)

  implicit object ListFunctor extends Functor[List] {
    def fmap[A, B](f: A => B): List[A] => List[B] =
      as => as map f
  }
}
scala> fmap(List(1, 2, 3))(x => x + 1)
res0: List[Int] = List(2, 3, 4)

Just to clarify, according to my understanding ListFunctor implements the 1-arg fmap in GenericFunctor while the code in the repl transcript calls the fmap in Trait Functor, which in turn calls an fmap implementation (e.g. in ListFunctor).

This doesn't change the overall question, just thought it would help people trying to provide answers. Any insights provided would be appreciated.

like image 217
Dave L. Avatar asked Sep 27 '11 15:09

Dave L.


1 Answers

In your example Functor is an endofunctor in the category of Scala types with Function1 as arrows.

There are other categories. For example, imagine a category in which the objects are Scala types, and there is an arrow A >~> B if B is a subtype of A. This category in Scalaz is called Liskov. There is a "forgetful" functor from the Liskov category to the Function1 category:

import scalaz._
import Scalaz._
trait Forget[F[-_]] extends GenericFunctor[>~>, Function1, F] {
  def fmap[A, B](f: A >~> B): F[A] => F[B] = fa => f.subst(fa)
}

Note that you can build some interesting functors by fixing one or more of the arguments to GenericFunctor. For example...

A constant functor maps every object in one category to a single object in another:

type ConstantFunctor[->>[_, _], ->>>[_, _], C] =
  GenericFunctor[->>,->>>,({type F[x] = C})#F]
// def fmap[A, B](f: A ->> B): C ->>> C

An endofunctor maps a category to itself:

type EndoFunctor[->>[_, _], F[_]] = GenericFunctor[->>, ->>, F]
// def fmap[A, B](f: A ->> B): F[A] ->> F[B]

An identity functor maps every object and arrow to itself:

type IdentityFunctor[->>[_, _]] = EndoFunctor[->>, ({type F[x] = x})#F]
// def fmap[A, B](f: A ->> B): A ->> B

And of course, your Functor trait is just an EndoFunctor in the Function1 category.

type Functor[F[_]] = EndoFunctor[Function1, F]
// def fmap[A, B](f: A => B): F[A] => F[B]
like image 53
Apocalisp Avatar answered Oct 25 '22 23:10

Apocalisp