- About Scala
- Documentation
- Code Examples
- Software
- Scala Developers
Compiler error, probably type inference bug with path-dependent types
Fri, 2011-10-21, 19:56
Hi,
I hit some issue while workin with metascala¹, but the big problem is that I'm unable to supply a minimized stand-alone example.
Some example:
The simplified Units object looks like this:
It looks like the problem is that the type inferencer can proof in all situations that the type alias is valid for the inferenced type, except when a path-dependent type appears (#Neg).
As far as I have understood
|
|
V
metascala.Units.Quantity[_1, _1]
|
|
V
Foo
while it gets stuck in the first step in the Subtractables case.
I hit some issue while workin with metascala¹, but the big problem is that I'm unable to supply a minimized stand-alone example.
Some example:
package metascala.test
object UnitsTest extends App { import metascala.Units._ val length1 = m(23) val length2 = m(17) val area: Area = length1 * length2 // Works, Quantity[_2, _0] val time = s(42) val foo: Foo = time * length1 // Works, Quantity[_1, _1] //val speed: Speed = length1 / time // Doesn't work, Quantity[_1, _1#Neg] /* type mismatch; found : metascala.Units.Quantity[metascala.Subtractables.-[metascala.Integers._1,metascala.Integers._0], metascala.Subtractables.-[metascala.Integers._0,metascala.Integers._1]] required: metascala.Units.Speed */}
The simplified Units object looks like this:
package metascala
object Units { import Integers._ import Addables._ import Subtractables._
trait Unit { type M <: MInt type S <: MInt } final class TUnit[_M <: MInt, _S <: MInt] { type M = _M type S = _S } case class Quantity[M <: MInt, S <: MInt](value : Double) { type This = Quantity[M, S] def *[M2 <: MInt, S2 <: MInt](m : Quantity[M2, S2]) = Quantity[M + M2, S + S2](value * m.value) def /[M2 <: MInt, S2 <: MInt](m : Quantity[M2, S2]) = Quantity[M - M2, S - S2](value / m.value) def apply(v : Double) = Quantity[M, S](v * value) } val m = Quantity[_1, _0](1) val s = Quantity[_0, _1](1) type Length = Quantity[_1, _0] type Time = Quantity[_0, _1] type Area = Quantity[_2, _0] type Speed = Quantity[_1, _1#Neg] type Foo = Quantity[_1, _1]}
It looks like the problem is that the type inferencer can proof in all situations that the type alias is valid for the inferenced type, except when a path-dependent type appears (#Neg).
As far as I have understood
val foo: Foo = time * length1metascala.Units.Quantity[metascala.Addables.+[metascala.Integers._0,metascala.Integers._1], metascala.Addables.+[metascala.Integers._1,metascala.Integers._0]]
works like this:
|
|
V
metascala.Units.Quantity[_1, _1]
|
|
V
Foo
while it gets stuck in the first step in the Subtractables case.
Sat, 2011-10-22, 12:47
#2
Re: Compiler error, probably type inference bug with path-depend
This gets even weirder.
It looks like it has nothing to do with the path-dependent types, I could reproduce it without going into the "negative" space.
I use a simplified version, which only uses one unit, length, to make things shorter. Have a look:
val length: Quantity[_1] = 5 m //Works
val area: Quantity[_2] = length * length //Works, too.
val length2: Quantity[_1] = area / length //Doesn't work!
The code for Addables/Subtractables is pretty much the same (and equivalent).
I really wonder what's the problem here...
Any ideas?
It looks like it has nothing to do with the path-dependent types, I could reproduce it without going into the "negative" space.
I use a simplified version, which only uses one unit, length, to make things shorter. Have a look:
val length: Quantity[_1] = 5 m //Works
val area: Quantity[_2] = length * length //Works, too.
val length2: Quantity[_1] = area / length //Doesn't work!
The code for Addables/Subtractables is pretty much the same (and equivalent).
I really wonder what's the problem here...
Any ideas?
Fri, 2011-11-04, 19:47
#3
Re: Compiler error, probably type inference bug with path-depend
I was able to produce a smaller example.
This code shows the problem:
object UnitsTest extends App {
import Units._
import Integers._
val length1 = m(23)
val length2 = m(17)
val area: Quantity[_2, _0] = length1 * length2 // Works
val time = s(42)
val foo: Quantity[_1, _1] = time * length1 // Works
val speed: Quantity[_1, _1#Neg] = length1 / time // Doesn't work
/*
type mismatch;
found :
Units.Quantity[Subtractables.-[Integers._1,Integers._0],
Subtractables.-[Integers._0,Integers._1]]
required:
Units.Quantity[Integers._1,Integers._0]
*/
val lengt3: Quantity[_1, _0] = length1 * length2 / length1 // Doesn't work
/*
type mismatch;
found :
Units.Quantity[Subtractables.-[Addables.+[Integers._1,Integers._1],Integers._1],
Subtractables.-[Addables.+[Integers._0,Integers._0],Integers._0]]
required:
Units.Quantity[Integers._1,Integers.MNeg[Integers.MSucc[Integers._0]]]
*/
}
Backing code is this:
object Units {
import Integers._
import Addables._
import Subtractables._
trait Unit {
type M <: MInt
type S <: MInt
}
final class TUnit[_M <: MInt, _S <: MInt] {
type M = _M
type S = _S
}
case class Quantity[M <: MInt, S <: MInt](value : Double) {
type This = Quantity[M, S]
def *[M2 <: MInt, S2 <: MInt](m : Quantity[M2, S2]) = Quantity[M + M2, S + S2](value * m.value)
def /[M2 <: MInt, S2 <: MInt](m : Quantity[M2, S2]) = Quantity[M - M2, S - S2](value / m.value)
def apply(v : Double) = Quantity[M, S](v * value)
}
val m = Quantity[_1, _0](1)
val s = Quantity[_0, _1](1)
}
object Addables {
trait Addable {
type AddType <: Addable
type Add[T <: AddType] <: AddType
}
type +[A1 <: Addable, A2 <: A1#AddType] = A1#cdcdcd[A2]
}
object Subtractables {
trait Subtractable {
type SubType <: Subtractable
type Sub[T <: SubType] <: SubType
}
type -[S1 <: Subtractable, S2 <: S1#SubType] = S1#Sub[S2]
}
object Comparables {
import Booleans._
trait Comparable {
type CompareType <: Comparable
type LessThan[T <: CompareType] <: Bool
}
}
object Booleans {
sealed trait Bool {
type Not <: Bool
}
final class True extends Bool {
type Not = False
}
final class False extends Bool {
type Not = True
}
}
object Integers {
import Visitables._
import Addables._
import Subtractables._
trait NatVisitor extends TypeVisitor {
type Visit0 <: ResultType
type VisitSucc[Pre <: Nat] <: ResultType
}
trait IntVisitor extends NatVisitor {
type VisitNeg[Pos <: Nat] <: ResultType
}
sealed trait MInt extends Visitable[IntVisitor] with Addable with Subtractable {
type AddType = MInt
type SubType = MInt
type Add[I <: MInt] <: MInt
type Neg <: MInt
type Succ <: MInt
type Pre <: MInt
}
sealed trait Nat extends MInt {
type Accept[V <: IntVisitor] = AcceptNatVisitor[V]
type AcceptNatVisitor[V <: NatVisitor] <: V#ResultType
}
final class _0 extends Nat {
type Add[I <: MInt] = I
type AcceptNatVisitor[V <: NatVisitor] = V#Visit0
type Neg = _0
type Succ = MSucc[_0]
type Pre = Succ#Neg
}
sealed trait Pos extends Nat
final class MSucc[P <: Nat] extends Pos {
type This = MSucc[P]
type Add[N <: MInt] = P#cdcdcd[N]#Succ
type AcceptNatVisitor[V <: NatVisitor] = V#VisitSucc[P]
type Neg = MNeg[This]
type Pre = P
type Succ = MSucc[This]
}
final class MNeg[N <: Pos] extends MInt {
type Add[N <: MInt] = N#cdcdcd[N#Neg]#Neg
type Accept[V <: IntVisitor] = V#VisitNeg[N]
type Neg = N
type Succ = N#Pre#Neg
type Pre = N#Succ#Neg
}
type _1 = MSucc[_0]
type _2 = MSucc[_1]
type _3 = MSucc[_2]
val _0 = new _0
val _1 = new _1
val _2 = new _2
val _3 = new _3
}
object Nats {
import Utils._
import Booleans._
import Addables._
import Comparables._
import Visitables._
trait NatVisitor extends TypeVisitor {
type Visit0 <: ResultType
type VisitSucc[Pre <: Nat] <: ResultType
}
sealed trait Nat extends Addable with Comparable {
type CompareType = Nat
type Pre
type Is0 <: Bool
type Add[T <: Nat] <: Nat
type AddType = Nat
type Accept[N <: NatVisitor] <: N#ResultType
type Equals[N <: Nat] <: Bool
type LessThan[N <: Nat] <: Bool
def toInt : Int
}
final class _0 extends Nat {
type Pre = Invalid
type Is0 = True
type Add[N <: Nat] = N
type Accept[N <: NatVisitor] = N#Visit0
type Equals[N <: Nat] = N#Is0
type LessThan[N <: Nat] = N#Is0#Not
def toInt = 0
}
final case class Succ[P <: Nat](toInt : Int) extends Nat {
type Pre = P
type Is0 = False
type Add[N <: Nat] = Succ[P#cdcdcd[N]]
type Accept[N <: NatVisitor] = N#VisitSucc[P]
trait EqualsVisitor extends NatVisitor {
type ResultType = Bool
type Visit0 = False
type VisitSucc[Pre <: Nat] = P#Equals[Pre]
}
type Equals[N <: Nat] = N#Accept[EqualsVisitor]
trait LessThanVisitor extends NatVisitor {
type ResultType = Bool
type Visit0 = False
type VisitSucc[Pre <: Nat] = P#LessThan[Pre]
}
type LessThan[N <: Nat] = N#Accept[LessThanVisitor]
def +[N <: Nat](n : N) : Add[N] = Succ[P#cdcdcd[N]](toInt + n.toInt)
}
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]
val _0 = new _0
val _1 = new _1(1)
val _2 = new _2(2)
val _3 = new _3(3)
}
object Visitables {
trait TypeVisitor {
type ResultType
}
trait Visitable[V <: TypeVisitor] {
type Accept[V2 <: V] <: V2#ResultType
}
}
object Utils {
final class Invalid
case class TypeToValue[T, VT](value : VT) {
def apply() = value
}
}
Any ideas what's the problem or how can I debug it?
Thanks and bye,
Simon
This code shows the problem:
object UnitsTest extends App {
import Units._
import Integers._
val length1 = m(23)
val length2 = m(17)
val area: Quantity[_2, _0] = length1 * length2 // Works
val time = s(42)
val foo: Quantity[_1, _1] = time * length1 // Works
val speed: Quantity[_1, _1#Neg] = length1 / time // Doesn't work
/*
type mismatch;
found :
Units.Quantity[Subtractables.-[Integers._1,Integers._0],
Subtractables.-[Integers._0,Integers._1]]
required:
Units.Quantity[Integers._1,Integers._0]
*/
val lengt3: Quantity[_1, _0] = length1 * length2 / length1 // Doesn't work
/*
type mismatch;
found :
Units.Quantity[Subtractables.-[Addables.+[Integers._1,Integers._1],Integers._1],
Subtractables.-[Addables.+[Integers._0,Integers._0],Integers._0]]
required:
Units.Quantity[Integers._1,Integers.MNeg[Integers.MSucc[Integers._0]]]
*/
}
Backing code is this:
object Units {
import Integers._
import Addables._
import Subtractables._
trait Unit {
type M <: MInt
type S <: MInt
}
final class TUnit[_M <: MInt, _S <: MInt] {
type M = _M
type S = _S
}
case class Quantity[M <: MInt, S <: MInt](value : Double) {
type This = Quantity[M, S]
def *[M2 <: MInt, S2 <: MInt](m : Quantity[M2, S2]) = Quantity[M + M2, S + S2](value * m.value)
def /[M2 <: MInt, S2 <: MInt](m : Quantity[M2, S2]) = Quantity[M - M2, S - S2](value / m.value)
def apply(v : Double) = Quantity[M, S](v * value)
}
val m = Quantity[_1, _0](1)
val s = Quantity[_0, _1](1)
}
object Addables {
trait Addable {
type AddType <: Addable
type Add[T <: AddType] <: AddType
}
type +[A1 <: Addable, A2 <: A1#AddType] = A1#cdcdcd[A2]
}
object Subtractables {
trait Subtractable {
type SubType <: Subtractable
type Sub[T <: SubType] <: SubType
}
type -[S1 <: Subtractable, S2 <: S1#SubType] = S1#Sub[S2]
}
object Comparables {
import Booleans._
trait Comparable {
type CompareType <: Comparable
type LessThan[T <: CompareType] <: Bool
}
}
object Booleans {
sealed trait Bool {
type Not <: Bool
}
final class True extends Bool {
type Not = False
}
final class False extends Bool {
type Not = True
}
}
object Integers {
import Visitables._
import Addables._
import Subtractables._
trait NatVisitor extends TypeVisitor {
type Visit0 <: ResultType
type VisitSucc[Pre <: Nat] <: ResultType
}
trait IntVisitor extends NatVisitor {
type VisitNeg[Pos <: Nat] <: ResultType
}
sealed trait MInt extends Visitable[IntVisitor] with Addable with Subtractable {
type AddType = MInt
type SubType = MInt
type Add[I <: MInt] <: MInt
type Neg <: MInt
type Succ <: MInt
type Pre <: MInt
}
sealed trait Nat extends MInt {
type Accept[V <: IntVisitor] = AcceptNatVisitor[V]
type AcceptNatVisitor[V <: NatVisitor] <: V#ResultType
}
final class _0 extends Nat {
type Add[I <: MInt] = I
type AcceptNatVisitor[V <: NatVisitor] = V#Visit0
type Neg = _0
type Succ = MSucc[_0]
type Pre = Succ#Neg
}
sealed trait Pos extends Nat
final class MSucc[P <: Nat] extends Pos {
type This = MSucc[P]
type Add[N <: MInt] = P#cdcdcd[N]#Succ
type AcceptNatVisitor[V <: NatVisitor] = V#VisitSucc[P]
type Neg = MNeg[This]
type Pre = P
type Succ = MSucc[This]
}
final class MNeg[N <: Pos] extends MInt {
type Add[N <: MInt] = N#cdcdcd[N#Neg]#Neg
type Accept[V <: IntVisitor] = V#VisitNeg[N]
type Neg = N
type Succ = N#Pre#Neg
type Pre = N#Succ#Neg
}
type _1 = MSucc[_0]
type _2 = MSucc[_1]
type _3 = MSucc[_2]
val _0 = new _0
val _1 = new _1
val _2 = new _2
val _3 = new _3
}
object Nats {
import Utils._
import Booleans._
import Addables._
import Comparables._
import Visitables._
trait NatVisitor extends TypeVisitor {
type Visit0 <: ResultType
type VisitSucc[Pre <: Nat] <: ResultType
}
sealed trait Nat extends Addable with Comparable {
type CompareType = Nat
type Pre
type Is0 <: Bool
type Add[T <: Nat] <: Nat
type AddType = Nat
type Accept[N <: NatVisitor] <: N#ResultType
type Equals[N <: Nat] <: Bool
type LessThan[N <: Nat] <: Bool
def toInt : Int
}
final class _0 extends Nat {
type Pre = Invalid
type Is0 = True
type Add[N <: Nat] = N
type Accept[N <: NatVisitor] = N#Visit0
type Equals[N <: Nat] = N#Is0
type LessThan[N <: Nat] = N#Is0#Not
def toInt = 0
}
final case class Succ[P <: Nat](toInt : Int) extends Nat {
type Pre = P
type Is0 = False
type Add[N <: Nat] = Succ[P#cdcdcd[N]]
type Accept[N <: NatVisitor] = N#VisitSucc[P]
trait EqualsVisitor extends NatVisitor {
type ResultType = Bool
type Visit0 = False
type VisitSucc[Pre <: Nat] = P#Equals[Pre]
}
type Equals[N <: Nat] = N#Accept[EqualsVisitor]
trait LessThanVisitor extends NatVisitor {
type ResultType = Bool
type Visit0 = False
type VisitSucc[Pre <: Nat] = P#LessThan[Pre]
}
type LessThan[N <: Nat] = N#Accept[LessThanVisitor]
def +[N <: Nat](n : N) : Add[N] = Succ[P#cdcdcd[N]](toInt + n.toInt)
}
type _1 = Succ[_0]
type _2 = Succ[_1]
type _3 = Succ[_2]
val _0 = new _0
val _1 = new _1(1)
val _2 = new _2(2)
val _3 = new _3(3)
}
object Visitables {
trait TypeVisitor {
type ResultType
}
trait Visitable[V <: TypeVisitor] {
type Accept[V2 <: V] <: V2#ResultType
}
}
object Utils {
final class Invalid
case class TypeToValue[T, VT](value : VT) {
def apply() = value
}
}
Any ideas what's the problem or how can I debug it?
Thanks and bye,
Simon
Fri, 2011-11-04, 20:37
#4
Re: Compiler error, probably type inference bug with path-depend
scalac -explaintypes results in this output:
UnitsTest.scala:12: error: type mismatch;
found : Units.Quantity[Subtractables.-[Integers._1,Integers._0],Subtractables.-[Integers._0,Integers._1]]
required: Units.Quantity[Integers._1,Integers.MNeg[Integers.MSucc[Integers._0]]]
val speed: Quantity[_1, _1#Neg] = length1 / time // Doesn't work
^
Units.Quantity[Subtractables.-[Integers._1,Integers._0],Subtractables.-[Integers._0,Integers._1]] <: Units.Quantity[Integers._1,Integers.MNeg[Integers.MSucc[Integers._0]]]?
Integers._1 <: Subtractables.-[Integers._1,Integers._0]?
Integers.MSucc[Integers._0] <: Nothing?
<notype> <: Nothing?
false
false
false
false
UnitsTest.scala:22: error: type mismatch;
found : Units.Quantity[Subtractables.-[Addables.+[Integers._1,Integers._1],Integers._1],Subtractables.-[Addables.+[Integers._0,Integers._0],Integers._0]]
required: Units.Quantity[Integers._1,Integers._0]
val lengt3: Quantity[_1, _0] = length1 * length2 / length1 // Doesn't work
^
Units.Quantity[Subtractables.-[Addables.+[Integers._1,Integers._1],Integers._1],Subtractables.-[Addables.+[Integers._0,Integers._0],Integers._0]] <: Units.Quantity[Integers._1,Integers._0]?
Integers._1 <: Subtractables.-[Addables.+[Integers._1,Integers._1],Integers._1]?
Integers.MSucc[Integers._0] <: Nothing?
<notype> <: Nothing?
false
false
false
false
two errors found
The <notype> and the Nothing look a bit suspicious ...
UnitsTest.scala:12: error: type mismatch;
found : Units.Quantity[Subtractables.-[Integers._1,Integers._0],Subtractables.-[Integers._0,Integers._1]]
required: Units.Quantity[Integers._1,Integers.MNeg[Integers.MSucc[Integers._0]]]
val speed: Quantity[_1, _1#Neg] = length1 / time // Doesn't work
^
Units.Quantity[Subtractables.-[Integers._1,Integers._0],Subtractables.-[Integers._0,Integers._1]] <: Units.Quantity[Integers._1,Integers.MNeg[Integers.MSucc[Integers._0]]]?
Integers._1 <: Subtractables.-[Integers._1,Integers._0]?
Integers.MSucc[Integers._0] <: Nothing?
<notype> <: Nothing?
false
false
false
false
UnitsTest.scala:22: error: type mismatch;
found : Units.Quantity[Subtractables.-[Addables.+[Integers._1,Integers._1],Integers._1],Subtractables.-[Addables.+[Integers._0,Integers._0],Integers._0]]
required: Units.Quantity[Integers._1,Integers._0]
val lengt3: Quantity[_1, _0] = length1 * length2 / length1 // Doesn't work
^
Units.Quantity[Subtractables.-[Addables.+[Integers._1,Integers._1],Integers._1],Subtractables.-[Addables.+[Integers._0,Integers._0],Integers._0]] <: Units.Quantity[Integers._1,Integers._0]?
Integers._1 <: Subtractables.-[Addables.+[Integers._1,Integers._1],Integers._1]?
Integers.MSucc[Integers._0] <: Nothing?
<notype> <: Nothing?
false
false
false
false
two errors found
The <notype> and the Nothing look a bit suspicious ...
You can verify the problem by downloading metascala from here: http://www.assembla.com/spaces/metascala/wiki
There is a deactivated test where the author seems to have hit the same problem:
http://trac.assembla.com/metascala/browser/src/metascala/test/UnitsTest....
I would be very grateful if someone could give me some hint ... or has some idea how a minimized example could look like.
Thanks and bye,
Simon