Dependent types not working for constructors?
Asked Answered
L

1

12

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?

Legman answered 6/8, 2013 at 10:15 Comment(5)
I've found this sentence in the SLS: 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. (5.3, p56) regarding formal value parameter clauses for the primary constructor.Jacie
Workaround: trait Foo[S :< Sys] { val system: S; val global: system.Global } .Tasset
@Tasset Yes, I am using a trait now, the constructor method is pretty ugly though, but it works.Legman
@Tasset I don't quite see how to change my code to implement it with the workaround. Could you explain, please?Gony
@Gony scalafiddle.io/sf/KzMwvEX/0Tasset
G
6

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.

Gilt answered 6/8, 2013 at 10:59 Comment(5)
Ok. The problem with secondary constructor, however, is that this way I won't be able to construct a Foo having a def global: system.Global (I haven't tried, but it sounds impossible)Legman
Hum, you're right, there is no easy way for Foo to expose these values. You would need at least a var and a cast, which is a bit sad, see edit.Gilt
Thanks for digging up the bug ticketLegman
Would a var even work? PDTs want "stable" identifiers like vals.Sulphonamide
Oh, I missed the edit with the intermediate lazy val. Ick :)Sulphonamide

© 2022 - 2024 — McMap. All rights reserved.