我想定义一个具有一些具有良好定义关系的属性的特征 - 例如,让我们说a * b = c
。这个想法是这个特征的实现可以提供其中的两个,并且具有自动派生的第三个属性的访问器。
(这与Haskell的类型类相同,如果我记得正确,如果你从(我不记得Haskell的类型类正确。)<
定义了Ord
,那么>=
将被实现为{{ 1}} - 虽然你可以定义任何函数子集,只要可以推断出其余部分。)
天真的方法实际上运作得很好:
! . <
此处,实施trait Foo {
// a * b = c
def a: Double = c / b
def b: Double = c / a
def c: Double = a * b
}
class FooOne(override val a: Double, override val b: Double) extends Foo
class FooTwo(override val a: Double, override val c: Double) extends Foo
和FooOne
是FooTwo
的完整实现,并按预期运行。到现在为止还挺好;这种方法允许类定义两个属性,并获得第三个属性“免费”。
然而,如果定义第三类,事情开始变得不那么乐观了:
Foo
这样编译得很好 - 但是,如果对其class FooFail(override val a: Double) extends Foo
或b
方法进行评估,它将导致堆栈溢出。
因此,天真的方法给出了Haskell类型类方法的推理方面,但我们没有编译时安全性。我想要的是编译器抱怨如果实现类定义的方法少于两个。显然,目前的语法还不够;我们需要将这些方法视为抽象的,尽管有一个默认实现,当且仅当依赖方法是非抽象的时才可以使用。
Scala是否公开了适当的语义来定义它? (如果有一种有点迂回的方式来定义它,我没有问题,类似于union types,因为我不知道语言中对此有任何一流的支持。)
如果没有,我将使用天真的方法,并仔细定义和测试我的类。但我真的认为这是类型系统应该能够捕获的东西(毕竟 - 它不是Ruby。:)。)。
答案 0 :(得分:8)
使用含义:
object test {
case class A(v : Double)
case class B(v : Double)
case class C(v : Double)
implicit def a(implicit b : B, c : C) = A(c.v / b.v)
implicit def b(implicit a : A, c : C) = B(c.v / a.v)
implicit def c(implicit a : A, b : B) = C(a.v * b.v)
def foo(implicit a : A, b : B, c : C) =
a + ", " + b + ", " + c
// Remove either of these and it won't compile
implicit val aa = A(3)
implicit val bb = B(4)
def main(args : Array[String]) {
println(foo)
}
}
答案 1 :(得分:5)
如何在Haskell获得安全保障?我对语言不太熟悉,但我可以做到
data Func = Func (Int -> Int)
instance Eq Func
instance Ord Func
compared = Func (\i -> i-1) < Func (\i -> i+1)
并在评估比较时获得堆栈溢出。
我可以想象scala中的解决方法,但是相当虚弱。首先,让Foo完全抽象
trait Foo { def a: Double; def b: Double; def c: Double }
然后为每个允许的方法定义组合创建mixin特征
trait FooWithAB extends Foo {def c : Double = a * b}
trait FooWithAC extends Foo {def b : Double = c / a}
trait FooWithBC extends Foo {def a : Double = c / b}
FooOne和FooTwo必须混合其中一个特征。在FooFail中,没有什么可以阻止你混合其中两个,但仍然失败,但你已经受到了一些警告。
是否有可能更进一步,禁止混合其中两种,带有一种幻像类型
trait Foo {
type t;
def a: Double; def b: Double; def c: Double
}
trait FooWithAB extends Foo {type t = FooWithAB; def c : Double = a * b}
trait FooWithAC extends Foo {type t = FooWithAC; def b : Double = c / a}
trait FooWithBC extends Foo {type t = FooWithBC; def c : Double = c / b}
这可以防止混合两个FooWithXX
,你无法定义
class FooFail(val a: Double) extends FooWithAC with FooWithAB
答案 2 :(得分:2)
稍微弱于您可能需要的解决方案如下:
trait Foo {
def a : Double
def b : Double
def c : Double
}
// Anything with two out of three can be promoted to Foo status.
implicit def ab2Foo(ab : { def a : Double; def b : Double }) =
new Foo { val a = ab.a; val b = ab.b; def c = ab.a * ab.b }
implicit def bc2Foo(bc : { def b : Double; def c : Double }) =
new Foo { val a = bc.c / bc.b; val b = bc.b; def c = bc.c }
implicit def ac2Foo(ac : { def a : Double; def c : Double }) =
new Foo { val a = ac.a; val b = ac.c / ac.a; def c = ac.c }
这里,任何具有三种a,b,c方法中的两种的类都可以查看并用作Foo。例如:
case class AB(a : Double, b : Double)
AB(5.0, 7.1).c // prints 35.5
但是如果你试试:
case class ABC(a : Double, b : Double, c : Double)
val x : Foo = ABC(1.0, 2.0, 3.0)
...你得到一个“含糊不清的”错误。
你的原始陈述的主要问题是这些类没有正确地从Foo继承,如果你想重用其他方法,这可能是一个问题。您可以通过使用包含这些其他方法的另一个特征FooImpl来解决它,并将隐式转换限制为FooImpl的子类型。