Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dependent types not working for constructors?

Path-dependent types are useful:

trait Sys {
  type Global
}
def foo[S <: Sys](system: S)(global: system.Global) = ()

Why doesn't this work for constructors?

class Foo[S <: Sys](val system: S)(val global: system.Global)

Or am I just doing it wrong?

like image 204
0__ Avatar asked Aug 06 '13 10:08

0__


1 Answers

This seems like a bug to me. Edit: found it, this is SI-5712.

Section §5.3 of the 2.9 SLS says:

(ps1 ) . . . (psn ) are formal value parameter clauses for the primary constructor of the class. The scope of a formal value parameter includes all subsequent parameter sections and the template t .

There is an exception:

However, a formal value parameter may not form part of the types of any of the parent classes or members of the class template t .

But it says it cannot be part of the types of any of the parent classes or members, not of any of the following parameter sections, so it does not seems to forbid path-dependent types between argument groups.

You can go around this with a secondary constructor:

class Foo[S <: Sys] private[this] () {
  def this(system: S)(global: system.Global) = this
}

Edit: this secondary constructor workaround is not very good: exposing system or global become very difficult because only the primary constructor can declare vals.

An example with a cast:

class Foo[S <: Sys] private[this] () {
  private[this] var _system: S = _
  private[this] var _global: system.Global = _

  def this(system0: S)(global0: system0.Global) = {
    this
    _system = system0
    _global = global0.asInstanceOf[system.Global]
  }

  lazy val global: system.Global = _global
  lazy val system: S = _system
}

But this is getting awful. @senia's suggestion is much better.

like image 185
gourlaysama Avatar answered Nov 08 '22 02:11

gourlaysama