首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Scalaz (或无形状)为每个子类创建Monoids

使用Scalaz (或无形状)为每个子类创建Monoids
EN

Stack Overflow用户
提问于 2015-12-09 01:19:25
回答 1查看 394关注 0票数 4

是否有可能为每个子类创建一元论?例如,

代码语言:javascript
复制
package currency 

final case class GBP[A: Monoid](amount: A)

object Implicits {
  implicit class CurrencyOps[A: Monoid](a: A) {
    def GBP = currency.GBP(a)
  }

  implicit def gbpMonoid[A: Monoid]: Monoid[GBP[A]] = new Monoid[GBP[A]] {
    override def zero =
      GBP(Monoid[A].zero)

    override def append(f1: GBP[A], f2: => GBP[A]): GBP[A] =
      GBP(Semigroup[A].append(f1.amount, f2.amount))
  }
}

test("GBP support plus") {
  1.GBP |+| 2.GBP shouldBe 3.GBP // passed
}

我想添加更多表示货币的案例类(例如USDEUR、.)

代码语言:javascript
复制
sealed trait Currency
final case class GBP[A: Monoid](amount: A) extends Currency
final case class USD[A: Monoid](amount: A) extends Currency
final case class EUR[A: Monoid](amount: A) extends Currency

因此,我不得不为新的案例类实现一元论。有点像样板。

代码语言:javascript
复制
implicit class CurrencyOps[A: Monoid](a: A) {
  def GBP = currency.GBP(a)
  def EUR = currency.EUR(a)
  def USD = currency.USD(a)
}

implicit def gbpMonoid[A: Monoid]: Monoid[GBP[A]] = new Monoid[GBP[A]] {
  override def zero =
    GBP(Monoid[A].zero)

  override def append(f1: GBP[A], f2: => GBP[A]): GBP[A] =
    GBP(Semigroup[A].append(f1.amount, f2.amount))
}

implicit def usdMonoid[A: Monoid]: Monoid[USD[A]] = new Monoid[USD[A]] {
  override def zero =
    USD(Monoid[A].zero)

  override def append(f1: USD[A], f2: => USD[A]): USD[A] =
    USD(Semigroup[A].append(f1.amount, f2.amount))
}

implicit def eurMonoid[A: Monoid]: Monoid[EUR[A]] = new Monoid[EUR[A]] {
  override def zero =
    EUR(Monoid[A].zero)

  override def append(f1: EUR[A], f2: => EUR[A]): EUR[A] =
    EUR(Semigroup[A].append(f1.amount, f2.amount))
}
EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2015-12-09 07:41:10

小建议

首先,我建议从case类中删除Monoid需求,因为它们将在每个Currency实例中包含隐式值。如果没有这个需求,您的包装程序可能会效率更高,甚至可以实现为价值类

代码语言:javascript
复制
  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构建简单的实现:

代码语言:javascript
复制
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)
    }
  }

并加以验证

代码语言:javascript
复制
import scalaz.syntax.monoid._
import scalaz.std.anyVal._

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

进一步改进

你完全可以摆脱无形体的。考虑这样的实现:

代码语言:javascript
复制
trait CurrencyUnit{
  def show(amounts: String) = s"$amounts $this"
}

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

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

代码语言:javascript
复制
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)
}

您可以根据您的需要配置

代码语言:javascript
复制
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功能很容易获得类型分类:

代码语言:javascript
复制
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
  }

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

代码语言:javascript
复制
import scalaz.syntax.monoid._
import scalaz.std.anyVal._

println((2.USD |+| 3.USD).shows) // $5
票数 11
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/34169144

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档