Debilski
Debilski

Reputation: 67838

Removing path-dependency from type check in Scala

Say we have these nested classes and an instantiation of A:

class A {
  case object B
  case class C(c: Int)
}
val a1 = new A()

Now I can check that a1.B is an instance of a1.B.type but how can I check that the type of a1.B is an instance of any A#B.type because the compiler won’t accept that syntax.

a1.B.isInstanceOf[a1.B.type]
res: Boolean = true

a1.B.isInstanceOf[A#B.type]
<console>:1: error: ']' expected but '.' found.
a1.B.isInstanceOf[A#B.type]
                     ^

For the case class it seems to work without problems:

a1.C(0).isInstanceOf[a1.C]
res: Boolean = true

a1.C(0).isInstanceOf[A#C]
res: Boolean = true

Follow-up question: When I have

val a1 = new A()
val a2 = new A()

is there a function that does an equality check without taking path-dependency into account? Eg. it should return true when comparing a1.B and a2.B. For example:

a1.B =#= a2.B
true

a1.C(0) =#= a2.C(0)
true

a1.C(0) =#= a2.C(1)
false

Edit: For clarification: Just introducing a common trait for B is not enough as I want to bet able to distinguish between case objects:

class A {
    trait BB
    case object B1 extends BB
    case object B2 extends BB
}
val a1 = new A
val a2 = new A

a1.B1 =#= a2.B1 // should be true
a1.B2 =#= a2.B2 // should be true
a1.B1 =#= a1.B2 // should be false
a1.B1 =#= a2.B2 // should be false

Now, the .hashCode (or .##) method seems to solve the problem:

a1.B1.## == a2.B1.## // true
a1.B1.## == a2.B2.## // false

but maybe there is a more elegant solution (I’d also like to be able to use a1.B1 in a pattern match for example).

Upvotes: 2

Views: 108

Answers (3)

ayvango
ayvango

Reputation: 5977

Just use full form for expressing existential types.

Here your example:

class A {
  case object B1
  case object B2
  case class C(c: Int)
}
val a1 = new A()

I've added another case object to demonstrate that they could be distinguished and so I have not write an obscure equivalent for AnyRef

type AB1 = a.B1.type forSome {val a : A}
type AB2 = a.B2.type forSome {val a : A}

scala> a1.B1.isInstanceOf[AB1]
res0: Boolean = true

scala> a1.B1.isInstanceOf[AB2]
res1: Boolean = false

I've introduced type aliases AB1 and AB2 for convenience. But that types may be inlined into isInstanceOf if there is a need.

Upvotes: 4

Debilski
Debilski

Reputation: 67838

I am not sure if this is purely a syntax thing but the following seems to solve it:

class A {
  case object B
  type BType = B.type
  case class C(c: Int)
}
val a1 = new A()
val a2 = new A()

a1.B.isInstanceOf[a1.B.type]
res: Boolean = true

a1.B.isInstanceOf[A#BType]
res: Boolean = true

And then I can kill the (2.11) compiler with

a1.B match {
  case _: A#BType => println("ABC")
}

> error: scala.MatchError: (?_1.type#B.type,a1.B.type) (of class scala.Tuple2)

In dotty it works though and interestingly enough a1.B.type and a1.BType are different:

List(a1, a2) foreach { a =>
  a.B match {
    case _: a1.B.type => println("Matches a1")
    case _: a2.B.type => println("Matches a2")
    case _: A#BType => println("Matches a1 and a2")
    case _: a1.BType => println("Matches a1 and a2")
    case _: a2.BType => println("Matches a1 and a2")
    case x => println(x)
  }
}

Upvotes: 0

Nabil A.
Nabil A.

Reputation: 3400

I think a1.B.isInstanceOf[A#B.type] is syntacticly wrong. There is no .type

How about using a inner trait

 class A{
    trait BTrait
    object B extends BTrait
  }

    val a1 = new A
    a1.B.isInstanceOf[A#BTrait] // this is true

if you want your inner instances to be equal independent of the outer instance,

a1.C(0) == a2.C(0)

is ture if you declare C as

final case class C()

this does not work for objects

Upvotes: 1

Related Questions