How to import inner classes without path dependency in Scala?

228 views Asked by At

TL&DR: Is it possible to (locally?) disable path-dependent typing? I'd like to issue a single import statement similar to import x._, but to make C refer to X#C rather than x.C (X being the type of x)?

I have a bunch of types:

class Button[M] { ... }
class Label[M] { ... }
...

And I happen to use them together passing the same type argument to all of them:

class UseSite[M] (
   btn1 : Button[M]
 , btn2 : Button[M]
 , lbl1 : Label[M]) {
  ...
}

I thought that it'd be nice to package all those types up so that I only need to pass the type parameter once:

class Swing[M] {
  class Button { ... }
  class Label { ...}
}

Then on the use site I'd like to be able to do this:

class WithSwing[M] {
  val swing = new Swing[M]
  import swing._
  class UseSite(
     btn1 : Button
   , btn2 : Button
   , lbl1 : Label) {
    ...
  }
}

However, this does not work properly because of path-dependent typing: Button refers to swing.Button instead of Swing[M]#Button so the external code has a very hard time passing the correct arguments to the UseSite constructor.

Is there a nice way to make Button refer to Swing[M]#Button rather than to swing.Button?

2

There are 2 answers

0
Kipton Barros On

One might imagine something like

import Swing[M]#_

to bring all the type members of type Swing[M] into scope, but this doesn't exist (import expects a value, not a type). For practical purposes, how about a type alias to reduce boiler-plate,

type S = Swing[M]
val b: S#Button = ... // Shorter than Swing[M]#Button
0
Miles Sabin On

Assuming that the path dependencies really are accidental, then rather than chase type projections all the way through your codebase, you could try something like this,

class Button[M]
class Label[M]

trait Swing[M] {
  type ButtonM = Button[M]
  type LabelM = Label[M]
}

class WithSwing[M] {
  val swing = new Swing[M] {}
  import swing._
  class UseSite(btn1 : ButtonM, btn2 : ButtonM, lbl1 : LabelM)
}

Outside WithSwing usage would look like,

val ws = new WithSwing[String]
import ws.swing._
new ws.UseSite(new ButtonM {}, new ButtonM {}, new LabelM {})

Inheritance increases brevity somewhat,

class WithSwing[M] extends Swing[M] {
  class UseSite(btn1 : ButtonM, btn2 : ButtonM, lbl1 : LabelM)
}

// Usage ...
val ws = new WithSwing[String]
import ws._
new ws.UseSite(new ButtonM {}, new ButtonM {}, new LabelM {})