我正在阅读以下书籍:

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

这应该构造一些东西吗?到目前为止,在讨论中还没有实现zeroMonoid[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是没有实现的特征。如何在不先定义opzero的情况下使用它?

最佳答案

为了编译该代码,您将需要以下内容:

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

Monoidal上下文中的zero是这样的元素:
Monoid[T].op(Monoid[T].zero, x) ==
Monoid[T].op(x, Monoid[T].zero) ==
x

Map的情况下,我假设Monoid将Maps与++结合在一起。然后,zero将简单地是Map.empty,这是Monoid[Map[Currency, BigDecimal]].zero最终简化为的内容。

编辑:comment的答案:

注意,这里根本不使用隐式转换。这是仅使用隐式参数的类型类模式。



这是一种实现方式,与我用++建议的方式不同。让我们来看一个例子。您如何将以下 map 组合在一起?
  • 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
      }
    

    关于Scala Monoid [Map [A​​,B]],我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/42600662/

    10-09 04:23