Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Higher order Functors in scala

So I've been trying to push my intuitions of functors to their limits by defining a higher order functor i.e. a, F that takes 1st order types as type argument, and functions and lifts functions on 1st order types to this higher context in scala something like

trait Functor1[F[_[_]] {
    def hmap[X[_], Y[_]] : (X ~> Y) => F[X] => F[Y]
}

I've been trying to define some of the map derivable functions of the normal functor e.g.

trait Functor[F[_]] {
  def map[A, B] : (A => B) => F[A] => F[B]

  // there's a few more besides this that are map derivable
  def distribute[A,B](fab: F[(A, B)]): (F[A], F[B])
}

but I can't write anything that type checks... I'm just playing but I wonder if anyone else has been down this road that's smarter than me

can a higher order functor be defined in scala ? if not then in haskell ?

like image 628
Mzk Levi Avatar asked May 10 '14 05:05

Mzk Levi


2 Answers

Not sure what are your goals, but this typechecks

  import scala.language.higherKinds

  trait Functor1[F[G[_]]]{
    def hmap[X[_], Y[_]]:(X ~> Y) => F[X] => F[Y] 
  }

  case class FId[Z,F[_]](f:F[Z])


  implicit def Functor1Id[Z] = new Functor1[({type L[G[_]]=FId[Z,G]})#L]{
    def hmap[X[_], Y[_]]:(X ~> Y) => FId[Z,X] => FId[Z,Y]= ??? 
  }

(I added the Z parameter because I wanted to avoid existentials and I had to use the "type lambda" trick)

Do you want to define a map for a "functor of a functor"? I think I did something similar (here called composition):

case class Comp[F[_],G[_],Z](unComp:F[G[Z]])
implicit def fcomp[F[_], G[_]](implicit ff:Functor[F], fg:Functor[G])=new Functor[({ type abs[A]=Comp[F,G,A]})#abs]{
  def fmap[A,B](fga:Comp[F,G,A])(f: A => B):Comp[F,G,B]= Comp(ff.fmap(fga.unComp)(fg.fmap(_)(f)))
}

I've been toying with functors in scala-reggen but I don't think I'm the smart one, as I mainly did it by fumbling around (and checking Scalaz for inspiration)

like image 51
GClaramunt Avatar answered Oct 28 '22 07:10

GClaramunt


/** Higher order functor */
trait HFunctor[F[_]] {
  def ffmap[G[_]: Functor, A, B](f: A => B): F[G[A]] => F[G[B]]
  def hfmap[G[_], H[_]](t: G ~> H): ({type λ[α] = F[G[α]]})#λ ~> ({type λ[α] = F[H[α]]})#λ
}

trait Functor[F[_]] { self =>

  def fmap[A, B](f: A => B): F[A] => F[B]

  // derived

  def map[A, B](x: F[A])(f: A => B): F[B]          = fmap(f)(x)
  def strengthL[A, B]: A => F[B] => F[(A, B)]      = a => f => fmap((x: B) => (a, x))(f)
  def strengthR[A, B]: F[A] => B => F[(A, B)]      = f => b => fmap((x: A) => (x, b))(f)
  def compose[G[_]](implicit e: Functor[G]): Functor[({ type λ[α] = F[G[α]]})#λ] =
    new Functor[({ type λ[α] = F[G[α]]})#λ] {
      def F = self;
      def G = e
      def fmap[A, B](f: A => B) = F.fmap(G.fmap(f))
    }

}

object Functor {
  @inline def apply[F[_]: Functor]: Functor[F] = iev
}

trait Coyoneda[F[_], A] { co =>

  type I

  def fi: F[I]

  def k: I => A

  final def run(implicit F: Functor[F]): F[A] = F.fmap(k)(fi)

  final def map[B](f: A => B): Coyoneda.Aux[F, B, I] =
   Coyoneda(fi)(f compose k)

  final def trans[G[_]](phi: F ~> G): Coyoneda[G, A] =
   Coyoneda(phi(fi))(k)
}

object Coyoneda {

  type Aux[F[_], A, B] = Coyoneda[F, A] { type I = B }

  def apply[F[_], B, A](x: F[B])(f: B => A): Aux[F, A, B] =
    new Coyoneda[F, A] {
      type I = B
      val fi = x
      val k = f
   }

implicit def coyonedaFunctor[F[_]]: Functor[({ type λ[α] = Coyoneda[F, α] })#λ] =
  new Functor[({ type λ[α] = Coyoneda[F, α] })#λ] {
    def fmap[A, B](f: A => B): Coyoneda[F, A] => Coyoneda[F, B] =
      x => apply(x.fi)(f compose x.k)
  }

implicit def coyonedaHFunctor: HFunctor[({ type λ[F[_]] = ({ type λ[α] = Coyoneda[F, α] })#λ })#λ] =
  new HFunctor[({ type λ[F[_]] = ({ type λ[α] = Coyoneda[F, α] })#λ })#λ] {
    def ffmap[G[_]: Functor, A, B](f: A => B): Coyoneda[G, A] => Coyoneda[G, B] = _.map(f)
    def hfmap[F[_], G[_]](t: F ~> G): (({ type λ[α] = Coyoneda[F, α] })#λ ~> ({ type λ[α] = Coyoneda[G, α] })#λ) = 
    new (({ type λ[α] = Coyoneda[F, α] })#λ ~> ({ type λ[α] = Coyoneda[G, α] })#λ) {
    def apply[A](x: Coyoneda[F, A]) = x.trans(t)
  }
}

def liftCoyoneda[F[_], A](fa: F[A]): Coyoneda[F, A] = apply(fa)(x => x)

def lowerCoyoneda[F[_]: Functor, A](c: Coyoneda[F, A]): F[A] = c.run

}
like image 1
Mzk Levi Avatar answered Oct 28 '22 07:10

Mzk Levi