Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Functor Instance for Type Constructor with Two Parameters in Scala

I have a class Foo with two parameters, and I am trying to write a Functor instance for Foo with the first parameter fixed, as follows:

object Scratchpad {

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

  case class Foo[X, Y](value: Y)

  implicit def fooInstances[X]: Functor[Foo[X, _]] =
    new Functor[Foo[X, _]] {
      def fmap[A, B](f: A => B): Foo[X, A] => Foo[X, B] =
        foo => Foo[X, B](f(foo.value))
    }
}

But the above code fails to compile, generating the following error:

Error:(9, 41) Scratchpad.Foo[X, _] takes no type parameters, expected: one
  implicit def fooInstances[X]: Functor[Foo[X, _]] =

I know Scalaz does something like this with their \/ type, but inspection of their source code reveals an odd ?, which doesn't compile for me:

implicit def DisjunctionInstances1[L]: Traverse[L \/ ?] with Monad[L \/ ?] with BindRec[L \/ ?] with Cozip[L \/ ?] with Plus[L \/ ?] with Optional[L \/ ?] with MonadError[L \/ ?, L] =

How does the Scalaz ? work, and how can I write a Functor instance for Foo?

like image 228
Fried Brice Avatar asked Jul 24 '17 02:07

Fried Brice


2 Answers

but inspection of their source code reveals an odd ?, which doesn't compile for me

? comes from the kind-projector project, which is a Scala compiler plugin you need to add to your build.sbt:

resolvers += Resolver.sonatypeRepo("releases")

addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.4")

Which prettifies the creation of type lambdas for you:

implicit def fooInstances[X]: Functor[Foo[X, ?]] =
  new Functor[Foo[X, ?]] {
    def fmap[A, B](f: A => B): Foo[X, A] => Foo[X, B] =
      foo => Foo[X, B](f(foo.value))
}

Remember that we can also use partial type application with type aliases:

implicit def fooInstances[X] = {
  type PartiallyAppliedFoo[A] = Foo[X, A]
  new Functor[PartiallyAppliedFoo] {
    override def fmap[A, B](f: (A) => B): (PartiallyAppliedFoo[A]) => PartiallyAppliedFoo[B] = foo => Foo[X, B](f(foo.value))
  }
}

Edit (05/04/2020)

Note the syntax for kind project has changed from ? to * for partially applied types, thus the above example should be:

sbt:

resolvers += Resolver.sonatypeRepo("releases")

addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.11.0")

Code:

implicit def fooInstances[X]: Functor[Foo[X, *]] =
  new Functor[Foo[X, *]] {
    def fmap[A, B](f: A => B): Foo[X, A] => Foo[X, B] =
      foo => Foo[X, B](f(foo.value))
}
like image 132
Yuval Itzchakov Avatar answered Oct 05 '22 14:10

Yuval Itzchakov


You're looking to partially apply a type-level constructor. Unfortunately, we can't directly do that. However, we can still do so indirectly using a little feature called Structural Types. In order to turn Foo from a two-argument type constructor into a one-argument type constructor, we'll define a type synonym inside of an anonymous, structural type.

implicit def fooInstances[X]: Functor[({ type T[A] = Foo[X, A] })#T] =
  new Functor[({ type T[A] = Foo[X, A] })#T] {
    // ...
  }

The braces {} in a type context define an anonymous type that we're exploiting to essentially create a lambda function at the type level. We define an anonymous type with an alias inside of it, then immediately evaluate that alias.

like image 33
Silvio Mayolo Avatar answered Oct 05 '22 13:10

Silvio Mayolo