我正在尝试使用“不透明类型”一章中的第二种方法(“模块抽象”)。代码旨在展示如何使Scala使用对数数字表示。
问题是,我不知道如何使用书中的代码。对我来说,这是行不通的。
trait Logarithms:
type Logarithm
// operations on Logarithm
def add(x: Logarithm, y: Logarithm): Logarithm
def mul(x: Logarithm, y: Logarithm): Logarithm
// functions to convert between Double and Logarithm
def make(d: Double): Logarithm
def extract(x: Logarithm): Double
// extension methods to use `add` and `mul` as "methods" on Logarithm
extension (x: Logarithm)
def toDouble: Double = extract(x)
def + (y: Logarithm): Logarithm = add(x, y)
def * (y: Logarithm): Logarithm = mul(x, y)
// I added
def myToDouble: Double = extract(x)
object LogarithmsImpl extends Logarithms:
type Logarithm = Double
// operations on Logarithm
def add(x: Logarithm, y: Logarithm): Logarithm = make(x.toDouble + y.toDouble)
def mul(x: Logarithm, y: Logarithm): Logarithm = x + y
// functions to convert between Double and Logarithm
def make(d: Double): Logarithm = math.log(d)
def extract(x: Logarithm): Double = math.exp(x)
// I added
object Logarithms:
def apply(d: Double) = LogarithmsImpl.make(d)书上说:
然而,这种抽象有点漏洞百出。我们必须确保只对抽象接口
Logarithms进行编程,而不直接使用LogarithmsImpl。直接使用LogarithmsImpl将使等式Logarithm = Double对用户来说是可见的,用户可能会意外地使用一个预期为对数双倍的Double。
现在我试着用它:
$ ~/Downloads/scala3-3.1.3/bin/scala
Welcome to Scala 3.1.3 (14.0.1, Java OpenJDK 64-Bit Server VM).
Type in expressions for evaluation. Or try :help.
scala> :load my/scala/logarithms1.scala
// defined trait Logarithms
// defined object LogarithmsImpl
// defined object Logarithms
scala> import Logarithms.*
scala> Logarithms(4)
val res0: LogarithmsImpl.Logarithm = 1.3862943611198906
scala> Logarithms(2)
val res1: LogarithmsImpl.Logarithm = 0.6931471805599453
scala> Logarithms(4).toDouble
val res2: Double = 1.3862943611198906
scala> Logarithms(4).myToDouble
-- [E008] Not Found Error: ----------------------------------------------------------------------------------------------------
1 |Logarithms(4).myToDouble
|^^^^^^^^^^^^^^^^^^^^^^^^
|value myToDouble is not a member of LogarithmsImpl.Logarithm, but could be made available as an extension method.
|
|The following import might fix the problem:
|
| import LogarithmsImpl.myToDouble
|
1 error found实际上,导入LogarithmsImpl并没有多大帮助:
scala> import LogarithmsImpl.*
scala> import Logarithms.*
scala> val ll: Logarithm = make(4.0)
val ll: LogarithmsImpl.Logarithm = 1.3862943611198906
scala> val lll: Logarithm = make(2.0)
val lll: LogarithmsImpl.Logarithm = 0.6931471805599453
scala> lll+ll
val res0: Double = 2.0794415416798357
scala> extract(lll+ll)
val res1: Double = 7.999999999999998最后一行显示加法是传统的浮点加法,而不是扩展中的覆盖。
,我错过了什么?
UPD本书代码中的另一个问题:行
def add(x: Logarithm, y: Logarithm): Logarithm = make(x.toDouble + y.toDouble)应该是
def add(x: Logarithm, y: Logarithm): Logarithm = make(x.myToDouble + y.myToDouble)或者更好的是:
def add(x: Logarithm, y: Logarithm): Logarithm = x + math.log1p(math.exp(y-x))UPD2,它看起来像特征和对象不是同伴,一个特征不是一个类,只有类可能是同伴。
发布于 2022-08-29 16:46:55
使用此抽象(我可以看到)的最合理方法如下:
@main
def main = {
val L = LogarithmsImpl
doStuffWithLogarithm(L)
}
def doStuffWithLogarithm(L: Logarithms): Double = {
import L.*
val a = L.make(1)
val b = L.make(2)
a.myToDouble
}它的要点是,您必须传递一些Logarithms实例,并使用它访问所有方法,这样类型别名等式就不会泄漏。
恐慌:https://scastie.scala-lang.org/KacperFKorban/4bnxghkMSzaPqn4vvy19QA/1
这就是为什么这不是一个很好的模式。以及为什么opaque types有用。
https://stackoverflow.com/questions/73531615
复制相似问题