Scala 值 class,用例
Scala value class, use cases
我知道 value class
在 scala 中内联编译时的操作。
可能是这样
case class A(i: Int) extends AnyVal {
def +(that: A) = A(this.i + that.i)
}
A(1) + A(2) // After compile it equals to 1 + 2
但这对我来说似乎没什么大不了的。
它可能会提高性能,但是,
调用this.i + that.i
似乎并不比i + i
慢多少
为什么我们需要 value class
在 scala 和任何用例中???
Value classes are a mechanism in Scala to avoid allocating runtime objects. This is accomplished through the definition of new AnyVal subclasses.
有关值 类 的更多信息,请参见此处 Value Classes
让我们看看 Scala 如何处理值 classes(-print
选项)。
case class A(i: Int) extends AnyVal {
def +(that: A) = A(this.i + that.i)
}
A(1) + A(2)
翻译为:
final def +$extension($this: Int, that: Int): Int = $this.+(that)
...
A.+$extension(1, 2)
如您所见,Scala 避免使用 class A
并简单地将 Int
添加到 Int
返回 Int
。同时:
case class A(i: Int) {
def +(that: A) = A(this.i + that.i)
}
A(1) + A(2)
翻译为:
def +(that: A): A = new A(this.i().+(that.i()))
...
new A(1).+(new A(2))
所以要计算 1 + 2
你需要实例化 class A
三次。
为什么要将单个值包装到附加的 class 中?
一个重要的用例是类型安全。假设您有可以使金钱倍增的功能,例如:
def multiply(factor: Int, amount: Int): Int = ???
这样做的问题是很容易混淆两个参数,从而错误地调用函数。使用值 classes,您可以创建一个 Money
类型并像这样重写函数:
case class Money(amount: Int) extends AnyVal
def multiply(factor: Int, amount: Money): Money = ???
现在有了你的特殊 Money
类型,如果你试图以错误的顺序传递参数,编译器会告诉你。
如果它不是一个值 class,人们可能会说在某些情况下增加的类型安全性不值得性能损失。但是,使用值 classes,您没有运行时开销(但有限制:http://docs.scala-lang.org/overviews/core/value-classes.html)。
实现相同目标的替代方法是在 scalaz 中取消装箱(无运行时开销)标记类型:http://eed3si9n.com/learning-scalaz/Tagged+type.html
请注意,例如 haskell 使用 newtype
表示相同的想法:https://wiki.haskell.org/Newtype
我知道 value class
在 scala 中内联编译时的操作。
可能是这样
case class A(i: Int) extends AnyVal {
def +(that: A) = A(this.i + that.i)
}
A(1) + A(2) // After compile it equals to 1 + 2
但这对我来说似乎没什么大不了的。
它可能会提高性能,但是,
调用this.i + that.i
似乎并不比i + i
为什么我们需要 value class
在 scala 和任何用例中???
Value classes are a mechanism in Scala to avoid allocating runtime objects. This is accomplished through the definition of new AnyVal subclasses.
有关值 类 的更多信息,请参见此处 Value Classes
让我们看看 Scala 如何处理值 classes(-print
选项)。
case class A(i: Int) extends AnyVal {
def +(that: A) = A(this.i + that.i)
}
A(1) + A(2)
翻译为:
final def +$extension($this: Int, that: Int): Int = $this.+(that)
...
A.+$extension(1, 2)
如您所见,Scala 避免使用 class A
并简单地将 Int
添加到 Int
返回 Int
。同时:
case class A(i: Int) {
def +(that: A) = A(this.i + that.i)
}
A(1) + A(2)
翻译为:
def +(that: A): A = new A(this.i().+(that.i()))
...
new A(1).+(new A(2))
所以要计算 1 + 2
你需要实例化 class A
三次。
为什么要将单个值包装到附加的 class 中?
一个重要的用例是类型安全。假设您有可以使金钱倍增的功能,例如:
def multiply(factor: Int, amount: Int): Int = ???
这样做的问题是很容易混淆两个参数,从而错误地调用函数。使用值 classes,您可以创建一个 Money
类型并像这样重写函数:
case class Money(amount: Int) extends AnyVal
def multiply(factor: Int, amount: Money): Money = ???
现在有了你的特殊 Money
类型,如果你试图以错误的顺序传递参数,编译器会告诉你。
如果它不是一个值 class,人们可能会说在某些情况下增加的类型安全性不值得性能损失。但是,使用值 classes,您没有运行时开销(但有限制:http://docs.scala-lang.org/overviews/core/value-classes.html)。
实现相同目标的替代方法是在 scalaz 中取消装箱(无运行时开销)标记类型:http://eed3si9n.com/learning-scalaz/Tagged+type.html
请注意,例如 haskell 使用 newtype
表示相同的想法:https://wiki.haskell.org/Newtype