定义一个依赖于自身的半群实例



。。。或者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

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

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中定义半群实例的正确方法,我会开始考虑放弃在这种语言中执行FP的想法。

有更好的方法吗?

以下操作应该很好:

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实例和|+|语法(也需要它们。

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

相关内容

  • 没有找到相关文章

最新更新