定义一个依赖自身的 Semigroup 实例
Defining a Semigroup instance that depends on itself
... 或必须编写 Scala 代码的 Haskell 程序员的不幸事故,第 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
)
}
关键是错误信息的这一部分:"and it lacks an explicit result type"。 Scala 中的递归方法必须具有明确的 return 类型,并且类似地类型 class 依赖于自身的实例(直接或间接地通过诸如 Map
实例和 |+|
语法之类的东西这种情况)也需要它们。
一般来说,将显式 return 类型放在 所有 隐式定义上是个好主意——不这样做会导致意外行为,如果您考虑并阅读规范(如本例),其中一些似乎是编译器中的错误。
... 或必须编写 Scala 代码的 Haskell 程序员的不幸事故,第 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
)
}
关键是错误信息的这一部分:"and it lacks an explicit result type"。 Scala 中的递归方法必须具有明确的 return 类型,并且类似地类型 class 依赖于自身的实例(直接或间接地通过诸如 Map
实例和 |+|
语法之类的东西这种情况)也需要它们。
一般来说,将显式 return 类型放在 所有 隐式定义上是个好主意——不这样做会导致意外行为,如果您考虑并阅读规范(如本例),其中一些似乎是编译器中的错误。