Defining a Semigroup instance that depends on itself

試著忘記壹切 提交于 2019-12-24 07:59:40

问题


... or mishaps of a Haskell programmer that has to code Scala, part 5.

I have the following structure in Scala:

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

And, using Cats, I would like to define a Semigroup instance of it.

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

This will result in the following error:

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

So, my guess was that since I'm defining the instance for Semigroup the Scala compiler cannot derive an instance for Semigroup of Map[String, ResourceTree]. This seems to be confirmed, since the following instance is compiles:

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

I'm really hoping I'm wrong because if this is the right way of defining an instance for a Semigroup in Scala I'll start considering the idea of giving up doing FP in this language.

Is there a better way?


回答1:


The following should work just fine:

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

The key is this part of the error message: "and it lacks an explicit result type". Recursive methods in Scala must have explicit return types, and similarly type class instances that depend on themselves (either directly or indirectly through something like the Map instance and |+| syntax in this case) also need them.

In general it's a good idea to put explicit return types on all implicit definitions—not doing so can lead to unexpected behavior, some of which makes sense if you think about it and read the spec (as in this case), and some of which just seems to be bugginess in the compiler.



来源:https://stackoverflow.com/questions/39621963/defining-a-semigroup-instance-that-depends-on-itself

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