我正在读一本书,内容如下:
sealed trait Currency
case object USD extends Currency
... other currency types
case class Money(m: Map[Currency, BigDecimal]) {
... methods defined
}
讨论继续将Money
上的某些类型的操作识别为Monoidal,因此我们要为Monoid
创建Money
。接下来会发现我无法正确解析的列表。
首先是zeroMoney
的定义。这样做如下:
final val zeroMoney: Money = Money(Monoid[Map[Currency, BigDecimal]].zero)
我在此处遇到的问题是Money
参数列表中的部分。具体来说
Monoid[Map[Currency, BigDecimal]].zero
这应该构建一些东西吗?到目前为止,在讨论中尚未实现zero
的{{1}}函数,这是什么意思?
以下是:
Monoid[Map[A,B]]
implicit def MoneyAdditionMonoid = new Monoid[Money] {
val m = implicitly(Monoid[Map[Currency, BigDecimal]])
def zero = zeroMoney
def op(m1: Money, m2: Money) = Money(m.op(m1.m, m2.m))
}
的定义在其他所有内容中都很好,所以这不是问题。但我仍然不明白op
给出了什么定义。这也给了我隐含的zeroMoney
同样的问题。
那么,m
实际上做了什么?我没有看到它是如何构造任何东西的,因为Monoid[Map[Currency, BigDecimal]]
是一个没有实现的特征。如果不首先定义Monoid
和op
,该如何使用?
答案 0 :(得分:3)
要编译此代码,您需要以下内容:
trait Monoid[T] {
def zero: T
def op(x: T, y: T): T
}
object Monoid {
def apply[T](implicit i: Monoid[T]): Monoid[T] = i
}
因此Monoid[Map[Currency, BigDecimal]].zero
将Monoid.apply[Map[Currency, BigDecimal]].zero
移至implicitly[Monoid[Map[Currency, BigDecimal]]].zero
,简化为zero
。
Monoid[T].op(Monoid[T].zero, x) ==
Monoid[T].op(x, Monoid[T].zero) ==
x
是
Map
如果是Monoid
,我会假设++
将地图与zero
结合起来。 Map.empty
只是Monoid[Map[Currency, BigDecimal]].zero
,这是Map[A, B]
最终简化为的内容。
编辑:回答comment:
请注意,此处未使用隐式转换 。这是仅使用隐式参数的类型类模式。
如果Monoid
是B
,则
Monoid
为++
这是一种方法,这与我在Map(€ → List(1, 2, 3), $ → List(4, 5))
建议的方式不同。我们来看一个例子吧。您如何期望将以下地图组合在一起:?
Map(€ → List(10, 15), $ → List(100))
Map(€ → List(1, 2, 3, 10, 15), $ → List(4, 5, 11))
您期望的结果可能是Monoid[List[Int]]
,这是唯一可能的,因为我们知道如何组合两个列表。我隐含使用的(Nil, :::)
是B
。对于一般类型B
,您还需要某些内容来将两个Monoid
粉碎在一起,这个内容称为Monoid[Map[A, B]]
!
为了完整性,这里是implicit def mm[A, B](implicit mb: Monoid[B]): Monoid[Map[A, B]] =
new Monoid[Map[A, B]] {
def zero: Map[A, B] = Map.empty
def op(x: Map[A, B], y: Map[A, B]): Map[A, B] =
(x.toList ::: y.toList).groupBy(_._1).map {
case (k, v) => (k, v.map(_._2).reduce(mb.op))
}.toMap
}
我猜这本书想要定义:
x