Functor Instance for Type Constructor with Two Parameters in Scala

让人想犯罪 __ 提交于 2019-11-30 14:44:47

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))
  }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!