在Scala中推断相互依赖的默认方法实现

And*_*yle 9 scala traits

我想定义一个具有一些具有良好定义关系的属性的特征 - 例如,为了说明这一点a * b = c.这个想法是这个特征的实现可以提供其中的两个并且具有自动导出的第三个属性的访问器.

(这与Haskell的类型类相同,如果我正确记住它们,如果你定义<的那样Ord,>=将被实现为! . <- 尽管你可以定义任何函数子集,只要可以推断其余部分.) (我不是t正确记住Haskell的类型类.)

天真的方法实际上运作得相当好:

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
Run Code Online (Sandbox Code Playgroud)

在这里,实现FooOneFooTwo是完全的实现Foo,并像预期的那样.到现在为止还挺好; 这种方法允许类定义两个属性并获得第三个属性"免费".

然而,如果定义第三类,事情开始变得不那么乐观了:

class FooFail(override val a: Double) extends Foo
Run Code Online (Sandbox Code Playgroud)

这编译得很好 - 但是,如果它b或它的c方法被评估,它将导致堆栈溢出.


因此,天真的方法给出了Haskell类型类方法的推理方面,但我们没有编译时安全性.我想要的是编译器抱怨如果实现类定义的方法少于两个.显然,目前的语法还不够; 我们需要将这些方法视为抽象的,尽管有一个默认实现,当且仅当依赖方法是非抽象的时才可以使用.

Scala是否公开了适当的语义来定义它?(如果有一种稍微迂回的方式来定义它,我没有问题,类似于联合类型,因为我不知道语言中对此有任何一流的支持).

如果没有,我将使用天真的方法,并仔细定义和测试我的类.但我真的认为这是类型系统应该能够捕获的东西(毕竟 - 它不是Ruby.:)).

Jes*_*erg 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)
  }
}
Run Code Online (Sandbox Code Playgroud)


Did*_*ont 5

你如何在Haskell获得安全保障?我对这门语言不太熟悉,但我能做到

data Func = Func (Int -> Int) 
instance Eq Func
instance Ord Func

compared = Func (\i ->  i-1) < Func (\i -> i+1)
Run Code Online (Sandbox Code Playgroud)

并且在评估比较时获得堆栈溢出.

我可以想象scala中的一种解决方法,但是相当虚弱.首先,让Foo完全抽象化

trait Foo { def a: Double; def b: Double; def c: Double }
Run Code Online (Sandbox Code Playgroud)

然后为允许的每个方法定义组合创建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}
Run Code Online (Sandbox Code Playgroud)

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}
Run Code Online (Sandbox Code Playgroud)

这样可以防止混合两种FooWithXX,你无法定义

class FooFail(val a: Double) extends FooWithAC with FooWithAB
Run Code Online (Sandbox Code Playgroud)