【问题标题】:Defining a Semigroup instance that depends on itself定义一个依赖于自身的 Semigroup 实例
【发布时间】:2016-09-21 16:31:59
【问题描述】:

... 或者 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 编译器无法派生Semigroup 的实例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 的想法。

有没有更好的办法?

【问题讨论】:

    标签: scala functional-programming typeclass scala-cats semigroup


    【解决方案1】:

    以下应该可以正常工作:

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

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

    【讨论】:

    • 我对“缺少明确的结果类型”感到困惑。我认为它指的是组合操作,而不是类本身。谢谢!我不禁想到我在用 Scala 编码时正在编写 FP 程序集......
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-03-07
    • 2012-09-26
    • 1970-01-01
    • 2020-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多