1

... 或者 Haskell 程序员不得不编写 Scala 代码的事故,第 5 部分。

我在 Scala 中有以下结构:

case class ResourceTree(
  resources: Map[String, ResourceTree]
) 

而且,使用 Cats,我想定义Semigroup它的一个实例。

object ResourceTreeInstances {
  implicit val semigroupInstance = new Semigroup[ResourceTree] {
    override def combine(x: ResourceTree, y: ResourceTree): ResourceTree = {
      ResourceTree(
        x.resources |+| y.resources
      )
    }
  }

这将导致以下错误:

value |+| is not a member of Map[String, ResourceTree]
[error]  Note: implicit value semigroupInstance is not applicable here because it comes after the application point and it lacks an explicit result type
[error]         x.resources |+| y.resource

所以,我的猜测是,由于我正在为SemigroupScala 编译器定义实例,因此无法派生Semigroupof的实例Map[String, ResourceTree]。这似乎得到了证实,因为编译了以下实例:

implicit val semigroupInstance = new Semigroup[ResourceTree] {
  override def combine(x: ResourceTree, y: ResourceTree): ResourceTree = {
    dummyCombine(x, y)
  }
}

// FIXME: see if there's a better way to avoid the "no instance of Semigroup" problem
def dummyCombine(x: ResourceTree, y: ResourceTree): ResourceTree = {
  ResourceTree(
    x.resources |+| y.resources
  )
}

我真的希望我错了,因为如果这是在 Scala 中为 Semigroup 定义实例的正确方法,我将开始考虑放弃用这种语言做 FP 的想法。

有没有更好的办法?

4

1 回答 1

4

以下应该可以正常工作:

import cats.Semigroup
import cats.instances.map._
import cats.syntax.semigroup._

case class ResourceTree(resources: Map[String, ResourceTree]) 

implicit val resourceTreeSemigroup: Semigroup[ResourceTree] =
  new Semigroup[ResourceTree] {
    def combine(x: ResourceTree, y: ResourceTree): ResourceTree =
      ResourceTree(
        x.resources |+| y.resources
      )
  }

关键是错误消息的这一部分:“它缺少明确的结果类型”。Scala 中的递归方法必须具有显式的返回类型,并且依赖于自身的类似类型的类实例(在这种情况下直接或间接通过Map实例和|+|语法之类的东西)也需要它们。

一般来说,将显式返回类型放在所有隐式定义上是一个好主意——不这样做会导致意外行为,如果您仔细考虑并阅读规范(如本例中),其中一些是有意义的,而其中一些似乎只是编译器中的错误。

于 2016-09-21T16:39:55.573 回答