Reputation: 2841
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
Reputation: 15086
When you insert the String
s 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 Char
s, 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 Char
s.
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