Reputation: 33409
This code should compile in Scala:
trait Pipe {
type Input
type Output
def apply(input: Input): Output
}
object Pipe {
trait Start extends Pipe {
override type Input = Seq[String]
}
abstract class Connect(val prev: Pipe) extends Pipe {
override type Input = prev.Output
}
}
object Pipe1 extends Pipe.Start {
override type Output = Int
override def apply(input: Input): Output =
input.length
}
object Pipe2 extends Pipe.Connect(prev = Pipe1) {
override type Output = Boolean
override def apply(input: Input): Output =
input%2 == 0
}
Pipe1
compiles fine but Pipe2
fails to compile with:
value % is not a member of Pipe2.this.Input
input%2 == 0
^
I know I can solve this with generics instead of dependent types but this should work as Pipe2.Input
should typecheck to be Int
from Pipe1.Output
Upvotes: 4
Views: 137
Reputation: 33409
@Andrey-Tyukin's answer works above. I also found this work around:
trait Pipe {
type Input
type Output
def apply(input: Input): Output
}
object Pipe {
trait Start extends Pipe {
override type Input = Seq[String]
}
abstract class Connect[O](val prev: Pipe.Emitting[O]) extends Pipe {
override type Input = O
}
type Emitting[O] = Pipe {type Output = O}
}
object Pipe1 extends Pipe.Start {
override type Output = Int
override def apply(input: Input): Output =
input.length
}
object Pipe2 extends Pipe.Connect(prev = Pipe1) {
override type Output = Boolean
override def apply(input: Input): Output =
input%2 == 0
}
Upvotes: 2
Reputation: 44908
The prev = Pipe
thing in the invocation of the constructor is not a proper path, the compiler cannot tie any type information to that, so you end up with a rather useless prev.Output =:= Input
for some indeterminate prev: Pipe
which has been set to something in the constructor.
With a minimal change, it works as expected:
trait Pipe {
type Input
type Output
def apply(input: Input): Output
}
object Pipe {
trait Start extends Pipe {
override type Input = Seq[String]
}
abstract class Connect extends Pipe {
val prev: Pipe
override type Input = prev.Output
}
}
object Pipe1 extends Pipe.Start {
override type Output = Int
override def apply(input: Input): Output =
input.length
}
object Pipe2 extends Pipe.Connect {
val prev = Pipe1
override type Output = Boolean
override def apply(input: Input): Output = input % 2 == 0
}
That's why it's called path dependent (not member dependent, not value dependent etc.).
Upvotes: 6