为 Scala 特征中的方法指定具体类型
Specify concrete type for methods in Scala trait
我想在 Scala 特征中定义一个方法,其中方法的参数和 return 类型对应于扩展特征的相同具体 class。我试过类似下面的东西:
trait A {
def foo(obj: this.type): this.type
}
final case class B(val bar: Int) extends A {
override def foo(obj: B): B = {
B(obj.bar + this.bar)
}
}
object Main {
def main(args: Array[String]) = {
val b1 = new B(0)
val b2 = new B(0)
val b3: B = b1.foo(b2)
}
}
但是,尝试编译此代码会出现以下错误:
Test.scala:5: error: class B needs to be abstract. Missing implementation for:
def foo(obj: B.this.type): B.this.type // inherited from trait A
case class B(val bar: Int) extends A {
^
Test.scala:6: error: method foo overrides nothing.
Note: the super classes of class B contain the following, non final members named foo:
def foo: ((obj: _1.type): _1.type) forSome { val _1: B }
override def foo(obj: B): B = {
^
2 errors
这里显然我对 Scala 类型系统有一些误解。 class B
中 foo
的签名是我想要的,但我不知道如何正确定义 A
中的方法(或者如果这是甚至可能)。 this question 似乎在问一些非常相似的问题,但我没有立即看出答案如何适用于我的情况。
类型注解this.type
表示你只能returnthis
。因此,在那种情况下,您可能不会 return B
的另一个实例,方法参数也是如此。
如果这只是关于 return 类型,一个解决方案是要求 foo
到 return 类型 A
的东西,[= 中的覆盖方法13=] 可以将 return 类型专门化为 return B
.
但是,由于您还有一个参数希望成为子类型的类型,因此您可以使用 Self Recursive Type。下面的例子编译并且应该做你想做的事。
trait A[S <: A[S]] {
def foo(obj: S): S
}
case class B(val bar: Int) extends A[B] {
override def foo(obj: B): B = {
B(obj.bar + 1)
}
}
考虑类型class解决方案
case class B(bar: Int)
// type class
trait Fooable[A] {
def foo(x: A, y: A): A
}
// proof that B satisfies Fooable constraints
implicit val fooableB: Fooable[B] = new Fooable[B] {
override def foo(x: B, y: B): B = B(x.bar + y.bar)
}
// a bit of syntax sugar to enable x foo y
implicit class FooableOps[A](x: A) {
def foo(y: A)(implicit ev: Fooable[A]) = ev.foo(x,y)
}
val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)
哪个 Scala 3 simplifies 到
case class B(bar: Int)
// type class
trait Fooable[A] {
extension (x: A) def foo (y: A): A
}
// proof that B satisfies Fooable constraints + syntactic sugar
given Fooable[B] with
extension (x: B) def foo (y: B): B = B(x.bar + y.bar)
val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)
查看 Scala 常见问题解答:How can a method in a superclass return a value of the “current” type?
我想在 Scala 特征中定义一个方法,其中方法的参数和 return 类型对应于扩展特征的相同具体 class。我试过类似下面的东西:
trait A {
def foo(obj: this.type): this.type
}
final case class B(val bar: Int) extends A {
override def foo(obj: B): B = {
B(obj.bar + this.bar)
}
}
object Main {
def main(args: Array[String]) = {
val b1 = new B(0)
val b2 = new B(0)
val b3: B = b1.foo(b2)
}
}
但是,尝试编译此代码会出现以下错误:
Test.scala:5: error: class B needs to be abstract. Missing implementation for:
def foo(obj: B.this.type): B.this.type // inherited from trait A
case class B(val bar: Int) extends A {
^
Test.scala:6: error: method foo overrides nothing.
Note: the super classes of class B contain the following, non final members named foo:
def foo: ((obj: _1.type): _1.type) forSome { val _1: B }
override def foo(obj: B): B = {
^
2 errors
这里显然我对 Scala 类型系统有一些误解。 class B
中 foo
的签名是我想要的,但我不知道如何正确定义 A
中的方法(或者如果这是甚至可能)。 this question 似乎在问一些非常相似的问题,但我没有立即看出答案如何适用于我的情况。
类型注解this.type
表示你只能returnthis
。因此,在那种情况下,您可能不会 return B
的另一个实例,方法参数也是如此。
如果这只是关于 return 类型,一个解决方案是要求 foo
到 return 类型 A
的东西,[= 中的覆盖方法13=] 可以将 return 类型专门化为 return B
.
但是,由于您还有一个参数希望成为子类型的类型,因此您可以使用 Self Recursive Type。下面的例子编译并且应该做你想做的事。
trait A[S <: A[S]] {
def foo(obj: S): S
}
case class B(val bar: Int) extends A[B] {
override def foo(obj: B): B = {
B(obj.bar + 1)
}
}
考虑类型class解决方案
case class B(bar: Int)
// type class
trait Fooable[A] {
def foo(x: A, y: A): A
}
// proof that B satisfies Fooable constraints
implicit val fooableB: Fooable[B] = new Fooable[B] {
override def foo(x: B, y: B): B = B(x.bar + y.bar)
}
// a bit of syntax sugar to enable x foo y
implicit class FooableOps[A](x: A) {
def foo(y: A)(implicit ev: Fooable[A]) = ev.foo(x,y)
}
val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)
哪个 Scala 3 simplifies 到
case class B(bar: Int)
// type class
trait Fooable[A] {
extension (x: A) def foo (y: A): A
}
// proof that B satisfies Fooable constraints + syntactic sugar
given Fooable[B] with
extension (x: B) def foo (y: B): B = B(x.bar + y.bar)
val b1 = B(1)
val b2 = B(41)
b1.foo(b2)
// B(42)
查看 Scala 常见问题解答:How can a method in a superclass return a value of the “current” type?