I'm just learning Scala so I apologize if this has already been discussed but the following seemed a bit odd to me:
scala> import scala.collection.immutable._
import scala.collection.immutable._
scala> val st1 = new WrappedString("Hello")
st1: scala.collection.immutable.WrappedString = Hello
scala> val st2 = new StringOps("Hello")
st2: scala.collection.immutable.StringOps = Hello
scala> st2 == st1
res0: Boolean = true
scala> st1 == st2
res1: Boolean = false
Can anyone explain this? I am using Scala version 2.10.0-M4. I haven't tried this with
anything other versions.
The reason why there occur differences is documented in ScalaDoc.
WrappedString:
The difference between this class and StringOps is that calling
transformer methods such as filter and map will yield an object of
type WrappedString rather than a String.
StringOps:
The difference between this class and WrappedString is that calling
transformer methods such as filter and map will yield a String
object, whereas a WrappedString will remain a WrappedString.
Both derive from collection.GenSeqLike which defines an equals method:
override def equals(that: Any): Boolean = that match {
case that: GenSeq[_] => (that canEqual this) && (this sameElements that)
case _ => false
}
Both implement the canEqual (derived from collection.IterableLike) which returns always true. But StringOps is not a collection.GenIterable:
scala> st1 sameElements st2
<console>:13: error: type mismatch;
found : scala.collection.immutable.StringOps
required: scala.collection.GenIterable[?]
st1 sameElements st2
^
Whereas WrappedString does:
scala> st2 sameElements st1
res13: Boolean = true
So it should obvious why the first case returns true and the other one false.
But why do both exist? I'm not totally sure why it is designed this way, but I think that's because of the fact that a String is not a collection in Scala. When we do some operation on a String like "abc" flatMap (_+"z") we wanna get back another String even though it is not always possible as shown by "abc" map (_+1). This is what StringOps does. But when we have some method def x[A](s: Seq[A]) = s.getClass how shall we call it with a String? In this case we need WrappedString:
scala> x("a")
res9: Class[_ <: Seq[Char]] = class scala.collection.immutable.WrappedString
So, StringOps is more lightweight as WrappedString. It allows us to call some methods on plain old java.lang.String without doing too much overhead. In 2.10 StringOps extend AnyVal. This means it is a value class and its existence can be optimized by scalac (no runtime overhead any more by wrapping the String). In contrast WrappedString allows us to handle a String as a real collection - as an IndexedSeq[Char].
Related
I'm using scala reflections to get all fields of a case class that are not methods. I then want to see if the type is a primitive type or string (or an option of these things). Here's a simple working example that checks if a field is a String.
scala> case class SimpleCase(val1: String)
defined class SimpleCase
scala> val members = typeOf[SimpleCase].members.filter(!_.isMethod).toList
members: List[reflect.runtime.universe.Symbol] = List(value val1)
scala> members.head.typeSignature
res57: reflect.runtime.universe.Type = String
scala> members.head.typeSignature == typeOf[String]
res58: Boolean = true
Works exactly the way that I would expect it to. Here's where things get weird - when I do the same thing with an Int, or any primitive type for that matter, the type checking test fails. Here's an example of said failure:
scala> case class SimpleCase(val1: Int)
defined class SimpleCase
scala> val members = typeOf[SimpleCase].members.filter(!_.isMethod).toList
members: List[reflect.runtime.universe.Symbol] = List(value val1)
scala> members.head.typeSignature
res59: reflect.runtime.universe.Type = scala.Int
scala> members.head.typeSignature == typeOf[Int]
res60: Boolean = false
scala> members.head.typeSignature == typeOf[scala.Int]
res61: Boolean = false
Also, typeOf[Int] prints out
scala> typeOf[Int]
res62: reflect.runtime.universe.Type = Int
The type signature of SimpleCase's val1 states that it is a scala.Int rather than typeOf[Int]'s type signature, which is Int. So I tried comparing val1 to both typeOf[Int] and typeOf[scala.Int] (even though I'm pretty sure they're the same thing) to no avail.
What's going on here? Is there a way around this?
You should compare types with =:= instead of ==.
The documentation says:
Type Equality can be checked with =:=. It's important to note that == should not be used to compare types for equality-- == can't check for type equality in the presence of type aliases, while =:= can.
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
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.
Why does each None (of different Option types) evaluate to true?
scala> val x: Option[String] = None
x: Option[String] = None
scala> val y: Option[Int] = None
y: Option[Int] = None
Both x and y are Option's of separate types, yet their None's equal each other.
scala> x == y
res0: Boolean = true
Why?
Without looking at the actual implementation, I would assume that None is actually a case object. Hence, there is exactly one None in your memory. So both are the same thing. And identity obviously implies equality.
As to the question, why you can actually compare the two: This is due to Scala's subtyping: Every Object has an equals method, and this method is what you are using with the == operator.
edit: I found the implementation On github you can see, that None is indeed a case object. There also is no equals() method, so you are using the one which is automatically generated for case classes. Hence the comment below about type erasure also applies to your Some() case.
And 1 == "string" returns false. Standard equality check in Scala is not type safe, i.e:
final def ==(arg0: Any): Boolean
If you want typesafety use === with Equal Typeclass from scalaz.
There are really two questions here:
Why does x == y typecheck in your REPL?
Why do they equal each other?
x == y compiles because == in Scala is not type safe:
scala> "x" == 1
res4: Boolean = false
So why do they equal each other? An Option in Scala is conceptually similar to an Algebraic Data Type in Haskell:
data Maybe a = Nothing | Just a
But if you look at the Option.scala source, you'll see that an Option is defined (simplifying somewhat) as:
sealed abstract class Option[+A] extends Product with Serializable
final case class Some[+A](x: A) extends Option[A]
case object None extends Option[Nothing]
On the Some side, you can see a case class with parameterized type +A - so a someish Option[Int] becomes Some[Int].
However, on the None side, you see an Option[Nothing] object, so a noneish Option[Int] and a noneish Option[String] both become an Option[Nothing] object, and hence equal each other.
As #TravisBrown points out, Scalaz catches this much earlier, at compile time:
scala> import scalaz._
scala> import Scalaz._
scala> val x: Option[String] = None
scala> val y: Option[Int] = None
scala> x === y
<console>:16: error: could not find implicit value for parameter F0: scalaz.Equal[Object]
x === y
scala> val z: Option[String] = None
scala> x === z
res3: Boolean = true
Option is (roughly speaking) implemented like this:
trait Option[+A]
case class Some[A](value: A) extends Option[A]
case object None extends Option[Nothing]
Because None is an object, (a singleton in java terms), there's only one instance of it, and because Nothing is at the very bottom of the type hierarchy, meaning it's a subtype of EVERY type in Scala (even null), the two Nones are effectively the same type
I'm running into a weird issue with reflection in Scala 2.10.0 Milestone 4 that I can't wrap my head around. First for the stuff that works the way I'd expect:
scala> import scala.reflect.runtime.universe._
import scala.reflect.runtime.universe._
scala> trait A[X]; trait B[Y] extends A[Y]
defined trait A
defined trait B
scala> typeOf[B[String]].parents
res0: List[reflect.runtime.universe.Type] = List(java.lang.Object, A[String])
scala> typeOf[B[String]].parents contains typeOf[A[String]]
res1: Boolean = true
Similarly (in the same session):
scala> trait D; trait E extends A[D]
defined trait D
defined trait E
scala> typeOf[E].parents
res2: List[reflect.runtime.universe.Type] = List(java.lang.Object, A[D])
scala> typeOf[E].parents contains typeOf[A[D]]
res3: Boolean = true
No surprises here: I can ask for a type's parents and get exactly what I expect. Now I essentially combine the two examples above:
scala> trait F extends A[String]
defined trait F
scala> typeOf[F].parents
res4: List[reflect.runtime.universe.Type] = List(java.lang.Object, A[String])
scala> typeOf[F].parents contains typeOf[A[String]]
res5: Boolean = false
I don't understand how this could be false. The same thing happens if I have F extend A[Seq[D]], A[Int], etc. What's the generalization I'm missing that would make this behavior make sense?
That's a bug. Right this morning I was going to investigate and fix it.
Edit. this appears to be an implementation detail of Scala reflection API leaking to the userland. It's not easy to fix, so for now we leave it as it is, but will look into the possibilities to improve.
In the meanwhile, to get correct results, one should always use =:= to compare types, not ==.
Another illustration of the strangeness:
scala> val atype = typeOf[A[String]]
atype: reflect.runtime.universe.Type = A[String]
scala> val atype2 = typeOf[F].parents(1)
atype2: reflect.runtime.universe.Type = A[String]
scala> typeOf[F].parents contains atype
res39: Boolean = false
scala> typeOf[F].parents contains atype2
res40: Boolean = true
I think you're seeing a bug similar to this one: https://issues.scala-lang.org/browse/SI-5959 (although I've confirmed that this oddness occurs outside of the REPL too).
I'm looking for a solution for testing if a value of any type is empty (or default). I.e. some kind of method on Any that tests if a String instance is equal to "", an Int - to 0, a Float - to 0f, a Boolean - to false, a List contains no items and so on for other types. Primarilly I'm interested whether some solution already exists in the standard library and if not how you would implement it. I believe this could be useful and if it doesn't exist in the standard library it should be suggested.
Use Zero type-class from Scalaz.
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> def isEmpty[A : Zero](value: A) = value == mzero[A]
isEmpty: [A](value: A)(implicit evidence$1: scalaz.Zero[A])Boolean
scala> isEmpty("")
res0: Boolean = true
scala> isEmpty(List())
res1: Boolean = true
scala> isEmpty(false)
res2: Boolean = true
scala> isEmpty("O HAI")
res3: Boolean = false
Link to a blog post of mine on a related topic.
Instead of passing around things of type T, you could pass around things of type Option[T], wrapping all valid things of type T, like so
val thing = 1
val thingOption = Some(thing)
and storing all invalid data as Nones, like so
val thingOption = None
Then, if you want to make a decision based on the value of thingOption, you can do it like this
thingOption match {
case None => // Whatever you want to do with defaults
case Some(x) => // Whatever you want to do with 'thing' if it isn't a default
}