【问题标题】:Functor Instance for Type Constructor with Two Parameters in ScalaScala中具有两个参数的类型构造函数的函子实例
【发布时间】:2017-12-29 12:39:14
【问题描述】:

我有一个带有两个参数的类Foo,我正在尝试为Foo 编写一个Functor 实例,并固定第一个参数,如下所示:

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))
    }
}

但是上面的代码编译失败,产生如下错误:

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

我知道 Scalaz 用他们的 \/ 类型做了类似的事情,但是检查他们的源代码发现一个奇怪的 ?,它不适合我:

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] =

Scalaz ? 是如何工作的,我如何为 Foo 编写 Functor 实例?

【问题讨论】:

标签: scala typeclass scalaz type-constructor kind-projector


【解决方案1】:

您正在寻找部分应用类型级构造函数。不幸的是,我们不能直接这样做。但是,我们仍然可以使用一个名为Structural Types 的小功能间接地做到这一点。为了将 Foo 从两个参数的类型构造函数转换为一个参数的类型构造函数,我们将在匿名的结构类型中定义一个类型同义词。

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

类型上下文中的大括号{} 定义了一个匿名类型,我们正在利用它在类型级别创建一个lambda 函数。我们定义了一个匿名类型,其中包含一个别名,然后立即评估该别名。

【讨论】:

    【解决方案2】:

    但是检查他们的源代码会发现一个奇怪的? 为我编译

    ? 来自 kind-projector 项目,这是一个 Scala 编译器插件,您需要添加到您的 build.sbt

    resolvers += Resolver.sonatypeRepo("releases")
    
    addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.9.4")
    

    这为您美化了类型 lambdas 的创建:

    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))
    }
    

    请记住,我们也可以使用带有类型别名的部分类型应用程序:

    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))
      }
    }
    

    编辑 (05/04/2020)

    注意 kind 项目的语法已从 ? 更改为 * 用于部分应用类型,因此上面的示例应该是:

    sbt:

    resolvers += Resolver.sonatypeRepo("releases")
    
    addCompilerPlugin("org.spire-math" %% "kind-projector" % "0.11.0")
    

    代码:

    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))
    }
    

    【讨论】:

    • 我希望我可以勾选两个答案,因为您解决了?。我想这是我在一个问题上提出两个问题的错。
    • @FriedBrice 没问题,只要你有答案,这才是最重要的 :)
    猜你喜欢
    • 2011-11-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多