B-6ren">
gpt4 book ai didi

scala - 编写隐式推导 "Laws"

转载 作者:行者123 更新时间:2023-12-01 10:44:18 27 4
gpt4 key购买 nike

我正在尝试设计一个玩具 CoversionRate 实现,以了解如何通过链式隐式定义对“定律”进行编码。编译以下代码:

case class ConversionRate[X <: Currency, Y <: Currency](rate: Double) extends AnyVal
object ConversionRate {
implicit val EUR2USD: ConversionRate[EUR.type, USD.type] = ConversionRate(1.1)
implicit val GBP2USD: ConversionRate[GBP.type, USD.type] = ConversionRate(1.21)

// "Laws"
implicit def inverse[X <: Currency, Y <: Currency](implicit cr: ConversionRate[X, Y]): ConversionRate[Y, X] =
ConversionRate[Y, X](1 / cr.rate)

implicit def transitivity[X <: Currency, Z <: Currency](implicit cr: ConversionRate[X, USD.type], cr2: ConversionRate[Z, USD.type]): ConversionRate[X, Z] =
ConversionRate(cr.rate * cr2.rate)

private val unit = ConversionRate(1)
implicit def self[X <: Currency]: ConversionRate[X,X] = unit.asInstanceOf
}

调用站点推导确实有效

val cr = implicitly[ConversionRate[EUR.type, GBP.type]]
diverging implicit expansion for type ConversionRate[EUR.type,GBP.type]
starting with method transitivity in object ConversionRate

我应该如何以可用于推导的方式编写定律?

最佳答案

隐式不能派生出任何你可以从你的规则中组合出来的可能类型,因为它很容易在你有无限种可能性的情况下结束:

id[X]: X => X
x: A => B
y: B => C

可以组成

z = x andThen y

但也许它可以生成

z = x andThen id[B] andThen y // or
z = id[A] andThen x andThen y // or
z = x andThen y andThen id[C] // or
z = id[A] andThen x andThen y andThen id[C] //
...

你知道这是怎么回事吗?

同时推导应该是明确的,当你从你的“原始”隐式开始并试图将它们组合成你的“目标”隐式时,应该只有一种可能的方法。一旦 Scala 发现存在歧义,它就会停止推导。

在您的示例中,您将 ConversionRate[X, USD]ConversionRate[Y, USD] 组合成 ConversionRate[X, Y] .但它不会阻止你做类似的事情:

ConversionRate[USD, USD] combine with
ConversionRate[USD, USD] combine with
ConversionRate[USD, USD] combine with...

实现此目的的一种方法是定义您的转换,以便只有一种方法可以实现,您不能轻易破坏,例如:

case class USDConversionRate[A <: Currency](rate: Double)
// implicit conversion rates

case class ConversionRate[X <: Currency, Y <: Currency](rate: Double) extends AnyVal
object ConversionRate {
implicit def combine[X <: Currency, Y <: Currency](
implicit
ev1: X =:!= USD.type, // available in shapeless
ev2: Y =:!= USD.type, // proves type inequality
ev3: X =:!= Y, // which we use to force only one way to derive each pair
xFromUSD: USDConversionRate[X],
yFromUSD: USDConversionRate[Y],
): ConversionRate[X, Y] = ...

implicit def toUSD[X <: Currency](
implicit X =:!= USD.type,
xFromUSD: USDConversionRate[X]
): ConversionRate[X, USD.type] = ...

implicit def fromUSD[X <: Currency](
implicit X =:!= USD.type,
xFromUSD: USDConversionRate[X]
): ConversionRate[USD.type, X] = ...

implicit def unit: ConversionRate[X, X] = ...
}

在这里,您将使用类型边界来切断所有循环。如果您使用例如,在原始代码中它们会出现inverse(因为任何派生 X -> Y 的东西都会与 inverse(inverse(X -> Y)) 冲突),等等,与 traverseunit 等。您必须保证扩展不会发散,并且禁止任何会导致循环或任何其他 2 种不同方式获得相同类型的东西。

您最大的问题是,使用这样的 2 个参数组合:A -> B,您总是可以尝试通过其他方式来完成此操作 A -> C -> B, A -> D -> B,所以最好的方法是消除任何可能的额外步骤。我的提议是,您仅从 USD -> X 转化率得出 A -> B 转化率,并防止每次转化与另一次转化发生冲突,完全禁止循环。

关于scala - 编写隐式推导 "Laws",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60742961/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com