Scala Monoid [Map [A​​,B]] [英] Scala Monoid[Map[A,B]]

查看:138
本文介绍了Scala Monoid [Map [A​​,B]]的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在读一本带有以下内容的书:

sealed trait Currency
case object USD extends Currency
... other currency types

case class Money(m: Map[Currency, BigDecimal]) {
  ... methods defined
}

讨论继续将Money上的某些类型的操作识别为Monoidal,因此我们想为Money创建Monoid.接下来是无法正确解析的列表.

首先是zeroMoney的定义.这样做如下:

final val zeroMoney: Money = Money(Monoid[Map[Currency, BigDecimal]].zero)

我在这里遇到的麻烦是Money参数列表中的部分.特别是

Monoid[Map[Currency, BigDecimal]].zero

这应该构造一些东西吗?到目前为止,在讨论中还没有为Monoid[Map[A,B]]实现zero函数的实现,这意味着什么?

这是下面的内容:

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最终简化为的内容.

回答评论:

请注意,此处根本不使用隐式转换 .这是仅使用隐式参数的类型类模式.

如果BMonoid

,则

Map[A, 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,您还需要 something 来将两个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
  }

I'm reading a book with the following:

sealed trait Currency
case object USD extends Currency
... other currency types

case class Money(m: Map[Currency, BigDecimal]) {
  ... methods defined
}

The discussion goes on to recognize certain types of operations on Money as being Monoidal so we want to create a Monoid for Money. What comes next though are listings I can't parse properly.

First is the definition of zeroMoney. This is done as follows:

final val zeroMoney: Money = Money(Monoid[Map[Currency, BigDecimal]].zero)

What I have trouble following here is the part inside the Money parameter list. Specifically the

Monoid[Map[Currency, BigDecimal]].zero

Is this supposed to construct something? So far in the discussion there hasn't been an implementation of the zero function for Monoid[Map[A,B]] so what does this mean?

Following this is the following:

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))
}

The definition of op is fine given everything else so that isn't a problem. But I still don't understand what zeroMoney is given its definition. This also gives me the same problem with the implicit m as well.

So, just what does Monoid[Map[Currency, BigDecimal]] actually do? I don't see how it constructs anything since Monoid is a trait with no implementation. How can it be used without defining op and zero first?

解决方案

For this code to compile, you would need something like the following:

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
}

So Monoid[Map[Currency, BigDecimal]].zero desugars into Monoid.apply[Map[Currency, BigDecimal]].zero, which simplifies to implicitly[Monoid[Map[Currency, BigDecimal]]].zero.

zero in the Monoidal context is the element such that

Monoid[T].op(Monoid[T].zero, x) ==
Monoid[T].op(x, Monoid[T].zero) ==
x

In the case of Map, I would assume the Monoid combines Maps with ++. The zero would then simply be Map.empty, which is what Monoid[Map[Currency, BigDecimal]].zero finally simplifies into.

Edit: answer to comment:

Note that implicit conversion is not used at all here. This is the type class pattern which uses only implicit parameters.

Map[A, B] is a Monoid if B is a Monoid

That's one way to do it, which is different from the one I suggested with ++. Let's see an example. How would you expect the following maps to be combined together:?

  • Map(€ → List(1, 2, 3), $ → List(4, 5))
  • Map(€ → List(10, 15), $ → List(100))

The results you would expect is probably Map(€ → List(1, 2, 3, 10, 15), $ → List(4, 5, 11)), which is only possible because we know how to combine two lists. The Monoid[List[Int]] I implicitly used here is (Nil, :::). For a general type B you would also need something to smash two Bs together, this something is called a Monoid!

For completeness, here is the Monoid[Map[A, B]] I'm guessing the book wants to define:

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]]的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆