问题描述
我在Scala中有以下结构:
case class ResourceTree(
资源: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
)
}
}
$ c
这会导致以下错误:
value | + |不是Map [String,ResourceTree]的成员
[error]注意:隐式值semigroupInstance在此处不适用,因为它位于应用程序点之后,并且缺少显式结果类型
[error] x。资源| + | y.resource
所以,我的猜测是因为我定义了 Semigroup Scala编译器无法为 Semigroup 的 Map [String,ResourceTree] 。这似乎已被证实,因为下面的实例正在编译:
隐式val semigroupInstance =新Semigroup [ResourceTree] {
覆盖def combine(x:ResourceTree,y:ResourceTree):ResourceTree = {
dummyCombine(x,y)
}
}
// FIXME:如果有更好的方法来避免没有半群的实例问题
def dummyCombine(x:ResourceTree,y:ResourceTree):ResourceTree = {
ResourceTree(
x.resources | + | y.resources
)
}
我真的希望我是因为如果这是在Scala中为Semigroup定义实例的正确方法,我将开始考虑放弃以这种语言执行FP的想法。
更好的方式?
解决方案以下应该工作得很好:
导入cats.Semigroup
导入cats.instances.map._
导入cats.syntax.semigroup._
案例类ResourceTree(资源:Map [String,ResourceTree])
隐式val resourceTreeSemigroup:Semigroup [ResourceTree] =
新Semigroup [ResourceTree] {
def combine(x:ResourceTree,y:ResourceTree):ResourceTree =
ResourceTree(
x.resources | + | y.resources
)
}
关键是这部分错误消息:它缺少一个明确的结果类型。 Scala中的递归方法必须具有显式的返回类型,类似的类型实例依赖于自己(直接或间接地通过类似于 Map 实例和
一般来说,将显式返回类型放在全部是个好主意隐式定义 - 如果不这样做可能会导致意外的行为,如果您仔细考虑并阅读规范(如本例中的内容),其中一些行为是有意义的,其中一些行为似乎在编译器中很笨重。
... 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.resourceSo, 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?
解决方案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.
这篇关于定义一个依赖于自身的Semigroup实例的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!