scalaz.std.MapInstances声明任何值为a的地图Semigroup本身就是a Monoid.由于Int是Semigroup,下面的代码工作:
def merge[K](maps : Iterator[Map[K, Int]]) : Map[K, Int] = maps.reduce(_ |+| _)
Run Code Online (Sandbox Code Playgroud)
但是,我很惊讶以下代码不起作用:
class Num(value : Int) extends Semigroup[Num] {
def append(x : Num, y : Num): Num = new Num(x.value + y.value)
}
def merge[K](maps : Iterator[Map[K, Num]]) : Map[K, Num] = maps.reduce(_ |+| _)
Run Code Online (Sandbox Code Playgroud)
任何人都可以向我解释为什么值为我的自定义Semigroup类的地图不被视为Monoids?
Tra*_*own 18
Semigroup是一个类型类,这意味着在表示数据的类中扩展它不是预期的用法.
如果你熟悉Java,认为之间的差异Comparable和Comparator.如果您Num在Java 中实现并希望支持比较Num值,则可以使用Num类实现Comparable[Num],也可以提供Comparator[Num]描述如何比较两个Num实例的类型值.
Semigroup就像Comparator,不是Comparable- 你不扩展它,你提供一个值来描述如何附加你的类型的实例.请注意,在您的版本中,value实例的参数未在以下实现中使用append:
import scalaz.Semigroup
class Num(value: Int) extends Semigroup[Num] {
def append(x: Num, y: Num): Num = new Num(x.value + y.value)
}
Run Code Online (Sandbox Code Playgroud)
相反,你会写这样的东西:
import scalaz.Semigroup
class Num(val value: Int)
object Num {
implicit val numSemigroup: Semigroup[Num] =
Semigroup.instance((a, b) => new Num(a.value + b.value))
}
Run Code Online (Sandbox Code Playgroud)
然后:
scala> def merge[K](maps: List[Map[K, Num]]): Map[K, Num] = maps.reduce(_ |+| _)
merge: [K](maps: List[Map[K,Num]])Map[K,Num]
scala> val merged = merge(List(Map("a" -> new Num(1)), Map("a" -> new Num(2))))
merged: Map[String,Num] = Map(foo -> Num@51fea105)
scala> merged("a").value
res5: Int = 3
Run Code Online (Sandbox Code Playgroud)
通过Semigroup[Num]在Num同伴对象中放入一个隐式的type值,我们说这是我们想要在需要将Num实例添加到一起时使用的操作.
使用这种模式,而不是继承具有类似于的优点在于优势Comparator拥有Comparable在Java中:您可以将数据类型定义所有你可能需要对数据进行操作的定义分开,你可以有多个实例,等等. Scala通过允许您将类型类的实例放入隐式作用域中来进一步利用这些优势,这样您就不必手动传递它们(尽管您仍然可以在需要或想要时执行此操作).
| 归档时间: |
|
| 查看次数: |
552 次 |
| 最近记录: |