EdgeCaseBerg
EdgeCaseBerg

Reputation: 2841

Structural Type doesn't call correct implementation?

I was under the impression that Structural Types use reflection under the hood (indicated by the need to tell the compiler to enable "-language:reflectiveCalls") and that whatever object matches the type will be using it's own version of the function. For example, if I call .contains on a Seq than it will use the Seq version, if I call it on a String then it will use the version defined in StringOps that it gets from SeqLike

So in scala 2.10.3, why does this happen:

Welcome to Scala version 2.10.3 (Java HotSpot(TM) 64-Bit Server VM, Java 1.7.0_79).
Type in expressions to have them evaluated.
Type :help for more information.

scala> type Containable = { def contains(elem:Any):Boolean }
defined type alias Containable

scala> val myMap: Map[String, Containable] = Map("A" -> "B", "C" -> Seq("A","B"))
myMap: Map[String,Containable] = Map(A -> B, C -> List(A, B))

scala> myMap("A").contains("B")
res0: Boolean = false

scala> myMap("C").contains("B")
res1: Boolean = true

scala> "B".contains("B")
res3: Boolean = true

As you can see, a String.contains(String) returns true for itself, but not if it's called while being interpretted as a Containable type, even though that matches the defined method in the StringOps class.

I have the feeling this has to do with the implementation of == since .contains documentation says:

true if this sequence has an element that is equal (as determined by ==) to elem, false otherwise.

this feeling is compounded by the results of checking the type via isInstanceOf

scala> val aVal = myMap("A")
aVal: Containable = B

scala> aVal.isInstanceOf[String]
res5: Boolean = false

scala> aVal.isInstanceOf[Seq[_]]
res6: Boolean = true

In response to the comment about compiler error, here is a screencast of my terminal showing this working

Upvotes: 0

Views: 47

Answers (1)

Jasper-M
Jasper-M

Reputation: 15086

When you insert the Strings into your Map, they get converted into a WrappedString, because String doesn't have a method with the signature you defined in Containable.

scala> implicitly[String => Containable]
res10: String => Containable = <function1>

scala> res10("s")
res11: Containable = s

scala> res11.getClass
res12: Class[_ <: AnyRef] = class scala.collection.immutable.WrappedString

In Scala 2.10.x WrappedString has a method contains(elem: Any): Boolean. It checks whether elem is an element of the collection on which contains is invoked. A WrappedString represents a collection of Chars, so that method will never return true if you give it a String. In scala 2.11.x that contains method has been changed so it only accepts Chars.

String itself has a method contains(elem: java.lang.CharSequence): Boolean. A String is a CharSequence so when you call contains("B") on a String that method will be called and the String will not get converted to a WrappedString.

Upvotes: 3

Related Questions