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
?
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.
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))
}
}
来源:https://stackoverflow.com/questions/45271911/functor-instance-for-type-constructor-with-two-parameters-in-scala