【发布时间】:2018-09-15 00:46:06
【问题描述】:
Scala 允许我们定义隐式参数。根据确切的类型选择正确的定义。在下面的示例中,为相同的 Money 类型定义了 2 个 monoid 实例,MoneyAdditionMonoid 用于累积,MoneyCompareMonoid 用于比较。
不清楚的是,Scala 编译器如何理解 maxDebitOnDay 和 sumBalances 函数中使用了哪个幺半群?能否请您向我强调一下,我无法理解。
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