Referencing path-dependent type of an argument in the type of another argument to a constructor

118 Views Asked by At

I have automata, which have dependent types (I would call them associated) for the states and labels of their transitions.

trait Automaton {
  type State
  type Label
  type Transition = (State, Label, State)
}

Now I want to write a class that takes as its argument an automaton and a function doing some sort of counting for transitions on that particular automaton. I would write:

class AutomataMagic(val aut: Automaton, val h: aut.Transition => Int) {

...
}

However, that doesn't compile. Is there any way I can specify that I want my function to do things to specifically this automaton's transition type?

2

There are 2 best solutions below

1
On BEST ANSWER

The error message says it all:

illegal dependent method type: parameter may only be referenced in a subsequent parameter section

As Luis Miguel Mejía Suárez said, you need to move h to another parameter group.

class AutomataMagic(val aut: Automaton)(val h: aut.Transition => Int) {}

Oddly enough, that doesn't work (probably because of this bug).

A (very naive) workaround would be this. It's not very satisfying, but it seems to work.

class AutomataMagic private(_h: Automaton#Transition => Int, val aut: Automaton) {
  val h: aut.Transition => Int = _h.asInstanceOf[aut.Transition => Int]
  def this(aut: Automaton)(h: aut.Transition => Int) =
    this(h.asInstanceOf[Automaton#Transition => Int], aut)
}

EDIT: Luis Miguel Mejía Suárez suggested using a trait with a companion object. Here's my interpretation of that:

trait AutomataMagic[A <: Automaton] {
  val aut: A
  val h: aut.Transition => Int
}
object AutomataMagic {
  def apply[A <: Automaton](_aut: A)(_h: _aut.Transition => Int) = new AutomataMagic[A] {
    val aut: _aut.type = _aut
    val h = _h
  }
}

By the way, the first snippet compiles in Scala 3.


Also, you really shouldn't use this:

class AutomataMagic[A <: Automaton](val aut: Automaton, val h: A#Transition => Int) {}

Not only is A#Transition unrelated to aut.Transition (at least make it aut: A), type projections are unsound and will be dropped in Scala 3. Maybe it won't matter to you in this specific case because Transition is invariant, but it is not safe.

1
On

I am an idiot. Apparently, you can just do this:

class AutomataMagic[A <: Automaton](val aut: Automaton, val h: A#Transition => Int) {}