freddie-freeloader February 2016

Scala GADT instance Functor

In Haskell I got:

data Foo a where
  Bar :: a -> Foo a
  Map :: (a -> b) -> Foo a -> Foo b

instance Functor Foo where
  fmap = Map

In Scala I came up with:

import cats.Functor

trait Foo[A]
case class Bar[A](t: A) extends Foo[A]
case class Map[A,B](fun: A => B,foo: Foo[A]) extends Foo[B]

implicit val fooFunctor: Functor[Foo] = new Functor[Foo] {
  def map[A,B](fa: Foo[A])(f: A => B) = Map(f,fa)
}

But Bar(40).map(_+2) gives me:

error: value map is not a member of Bar[Int]

I fairly new to Scala and don't know the way of inheritance that well.

What am I missing?

Answers


Huw February 2016

You need to upcast Bar(40) to Foo explicitly, and import the syntax implicits:

import cats.syntax.functor._
(Bar(40): Foo[Int]).map(_+2)

You need the upcast because Scala will infer the type Bar[Int] for the expression Bar(40) and this then interferes with finding the appropriate implicit that adds the map method. For this reason you sometimes see helper methods in the companion object to do the upcast for you:

object Foo {
  def bar[A](a: A): Foo[A] = Bar(a)
  // etc.
}


dth February 2016

I thought a little bit about how one could solve this problem. Basically, we want to provide the functor operations even if only a super type is a functor. It seems to be impossible to express, that some higher-kinded type is the super type of an other, e.g. something like this is not possible: def bla[F[_], R[_] <: F]

We can however provide a conversion from R[] to F[] implicitly together with the functor for B:

abstract class ProvidesFor[R,F[_],FF[_[_]],A](val value: FF[F]) {
  def convert(r: R): F[A]
}
implicit def providesFunctorForBar[A] =
  new ProvidesFor[Bar[A],Foo,Functor,A](Functor[Foo]) {
    override def convert(r: Bar[A]): Foo[A] = r
  }

So the implict val will provide us with a functor for Foo and a convertion from Bar to Foo.

Now we can provide an implicit converstion to Functor.Ops like this:

implicit class FunctorOps[R[_],F[_],A](target: R[A])(implicit tc: ProvidesFor[R[A],F,Functor,A])
      extends Functor.Ops[F,A] {
  override val self = tc.convert(target)
  override val typeClassInstance = tc.value
  override def map[B](f: A => B): F[B] = typeClassInstance.map(self)(f)
  override def imap[B](f: A => B)(g: B => A): F[B] = map(f)
}

Now this works as expected:

Bar(1).imap(_+2)(_+5)

We can also do the same for Map:

implicit def providesFunctorForMap[A,B] =
  new ProvidesFor[Map[A,B],Foo,Functor,B](Functor[Foo]) {
    override def convert(r: Map[A,B]): Foo[B] = r
  }

Map((_:Int) + 1,Bar(5)).map(_+2)

For some strange reason I had to implement map and imap when extending Functor.Ops even though these methods are not abstract. In fact it compiles fine when I do not implement them but it fails at runtime with an AbstractMethodError. So somehow the compiler thinks the implementations are there but they are not. I suspect they are using some kind of byte code optimizati

Post Status

Asked in February 2016
Viewed 2,679 times
Voted 4
Answered 2 times

Search




Leave an answer