【问题标题】:Implicit parameters with 2 instances of the same type具有 2 个相同类型实例的隐式参数
【发布时间】:2018-09-15 00:46:06
【问题描述】:

Scala 允许我们定义隐式参数。根据确切的类型选择正确的定义。在下面的示例中,为相同的 Money 类型定义了 2 个 monoid 实例,MoneyAdditionMonoid 用于累积,MoneyCompareMonoid 用于比较。

不清楚的是,Scala 编译器如何理解 maxDebitOnDaysumBalances 函数中使用了哪个幺半群?能否请您向我强调一下,我无法理解。

trait Monoid[T] {
  def zero: T
  def op(t1: T, t2: T): T
}

object Monoid {

  def apply[T](implicit monoid: Monoid[T]) = monoid

  implicit val IntAdditionMonoid = new Monoid[Int] {
    val zero = 0
    def op(i: Int, j: Int) = i + j
  }

  implicit val BigDecimalAdditionMonoid = new Monoid[BigDecimal] {
    val zero = BigDecimal(0)
    def op(i: BigDecimal, j: BigDecimal) = i + j
  }

  implicit def MapMonoid[K, V: Monoid] = new Monoid[Map[K, V]] {
    def zero = Map.empty[K, V]
    def op(m1: Map[K, V], m2: Map[K, V]) = m2.foldLeft(m1) { (a, e) =>
      val (key, value) = e
      a.get(key).map(v => a + ((key, implicitly[Monoid[V]].op(v, value)))).getOrElse(a + ((key, value)))
    }
  }

  final val zeroMoney: Money = Money(Monoid[Map[Currency, BigDecimal]].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))
  }

  object MoneyOrdering extends Ordering[Money] {
    def compare(a:Money, b:Money) = a.toBaseCurrency compare b.toBaseCurrency
  }

  import MoneyOrdering._
  import scala.math.Ordering

  implicit val MoneyCompareMonoid = new Monoid[Money] {
    def zero = zeroMoney
    def op(m1: Money, m2: Money) = if (gt(m1, m2)) m1 else m2
  }
}

package monoids.monoid

import java.util.Date

sealed trait TransactionType
case object DR extends TransactionType
case object CR extends TransactionType

sealed trait Currency
case object USD extends Currency
case object JPY extends Currency
case object AUD extends Currency
case object INR extends Currency

object common {
  type Amount = BigDecimal
}

import common._

case class Money(m: Map[Currency, Amount]) {
  def toBaseCurrency: Amount = ???
}

trait Analytics[Transaction, Balance, Money] {
  def maxDebitOnDay(txns: List[Transaction])(implicit m: Monoid[Money]): Money
  def sumBalances(bs: List[Balance])(implicit m: Monoid[Money]): Money
}

case class Transaction(txid: String, accountNo: String, date: Date, amount: Money, txnType: TransactionType, status: Boolean)

case class Balance(b: Money)

object Analytics extends Analytics[Transaction, Balance, Money] {
  import Monoid._

  final val baseCurrency = USD

  private def valueOf(txn: Transaction): Money = {
    if (txn.status) txn.amount
    else MoneyAdditionMonoid.op(txn.amount, Money(Map(baseCurrency -> BigDecimal(100))))
  }

  private def creditBalance(bal: Balance): Money = {
    if (bal.b.toBaseCurrency > 0) bal.b else zeroMoney
  }

  def maxDebitOnDay(txns: List[Transaction])(implicit m: Monoid[Money]): Money = {
    txns.filter(_.txnType == DR).foldLeft(m.zero) { (a, txn) => m.op(a, valueOf(txn)) }
  }

  def sumBalances(bs: List[Balance])(implicit m: Monoid[Money]): Money = 
    bs.foldLeft(m.zero) { (a, bal) => m.op(a, creditBalance(bal)) }
}

例子来自

功能反应域建模

书。

【问题讨论】:

  • 最好将此答案发布为您自己问题的实际答案,使其对其他人更具可读性和可见性。

标签: scala functional-programming implicit monoids


【解决方案1】:

答案在书的论坛里找到:Explicit dictionary passing technique in listing 4.3

我们必须显式传递隐式参数,所以:

ltx 是交易列表

maxDebitOnday(ltx)(Monoid.MoneyCompareMonoid) 

lbs 是一个余额列表

sumBalances(lbs)(Monoid.MoneyAdditionMonoid) 

【讨论】:

    猜你喜欢
    • 2014-02-13
    • 1970-01-01
    • 1970-01-01
    • 2021-03-16
    • 2017-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多