当前位置:  开发笔记 > 编程语言 > 正文

使用Scalaz(或Shapeless)为每个子类创建Monoids

如何解决《使用Scalaz(或Shapeless)为每个子类创建Monoids》经验,为你挑选了1个好方法。



1> Odomontois..:

小建议

首先,我想建议Monoid从案例类中删除需求,因为它们将在每个Currency实例中携带隐含值.如果没有这个要求,你的包装器可以更高效,甚至可以作为值类实现:

  sealed trait Currency extends Any
  final case class GBP[A](amount: A) extends AnyVal with Currency
  final case class USD[A](amount: A) extends AnyVal with Currency
  final case class EUR[A](amount: A) extends AnyVal with Currency

无形的实施

从这里,您可以shapeless根据需要构建简单的实现:

import scalaz._
import shapeless._

implicit def monoidCurrency[A, C[_] <: Currency]
(implicit monoid: Monoid[A], gen: Generic.Aux[C[A], A :: HNil]) =
  new Monoid[C[A]] {
    def zero: C[A] = gen.from(monoid.zero :: HNil)
    def append(f1: C[A], f2: => C[A]): C[A] = {
      val x = gen.to(f1).head
      val y = gen.to(f2).head
      gen.from(monoid.append(x, y) :: HNil)
    }
  }

并验证它

import scalaz.syntax.monoid._
import scalaz.std.anyVal._

println(2.USD |+| 3.USD) // USD(5)

进一步改进

你可以摆脱没有形状.考虑这样的实现:

trait CurrencyUnit{
  def show(amounts: String) = s"$amounts $this"
}

final case class Currency[A, U <: CurrencyUnit](amount: A) extends AnyVal

CurrencyUnit 现在不是类的问题,它只是编译时类型标记

implicit case object GBP extends CurrencyUnit
implicit case object USD extends CurrencyUnit{
  override def show(amounts: String) = s"$$$amounts "
}
implicit case object EUR extends CurrencyUnit

implicit class CurrencyOps[A](a: A) {
  def GBP = Currency[A, GBP.type](a)
  def EUR = Currency[A, EUR.type](a)
  def USD = Currency[A, USD.type](a)
}

您可以根据需要进行配置

import scalaz.syntax.show._

implicit def currencyShow[A: Show, U <: CurrencyUnit](implicit unit: U) =
  new Show[Currency[A, U]] {
    override def shows(f: Currency[A, U]) = unit.show(f.amount.shows)
  }

最重要的是通过scalaz.Isomorphism.Iso功能轻松派生类型类:

import Isomorphism._

implicit def currencyIso[A, U <: CurrencyUnit] = new (Currency[A, U] <=> A) {
  def to: (Currency[A, U]) => A = _.amount
  def from: (A) => Currency[A, U] = Currency[A, U]
}

implicit def currencyMonoid[A: Monoid, U <: CurrencyUnit] =
  new IsomorphismMonoid[Currency[A, U], A] {
    def G: Monoid[A] = implicitly
    def iso: Currency[A, U] <=> A = implicitly
  }

最后,您也可以验证此解决方案

import scalaz.syntax.monoid._
import scalaz.std.anyVal._

println((2.USD |+| 3.USD).shows) // $5

推荐阅读
sx-March23
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有