Comparing None's of Different Types - scala

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

Related

In scala, is it possible to have a two-elements only Set?

In the end, I want to have a case class Swap so that Swap(a, b) == Swap(b, a).
I thought I could use a Set of two elements, and it quite does the job :
scala> case class Swap(val s:Set[Int])
defined class Swap
scala> Swap(Set(2, 1)) == Swap(Set(1, 2))
res0: Boolean = true
But this allows for any number of elements, and I would like to limit my elements to two. I found the class Set.Set2, which is the default implementation for an immutable Set with two elements, but it doesn't work the way I tried, or variations of it :
scala> val a = Set(2, 1)
a: scala.collection.immutable.Set[Int] = Set(2, 1)
scala> a.getClass
res3: Class[_ <: scala.collection.immutable.Set[Int]] = class scala.collection.immutable.Set$Set2
scala> case class Swap(val s:Set.Set2[Int])
defined class Swap
scala> val swp = Swap(a)
<console>:10: error: type mismatch;
found : scala.collection.immutable.Set[Int]
required: Set.Set2[Int]
val swp = Swap(a)
^
So my questions are :
is there a way to use Set2 as I try ?
is there a better way to implement my case class Swap ? I read that one shouldn't override equals in a case class, though it was my first idea.
This is a generic implementation -
import scala.collection.immutable.Set.Set2
def set2[T](a: T, b: T): Set2[T] = Set(a, b).asInstanceOf[Set2[T]]
case class Swap[T](s: Set2[T])
Swap(set2(1,2)) == Swap(set2(2,1)) //true
The reason that your solution didn't work is because of the signature
Set(elems: A*): Set
In case of 2 elements the concrete type will be Set2 but the compiler doesn't know that so you have to cast it to Set2
You can always hide the implementation details of Swap, in this case you actually should.
You could implement it using Set or you could implement it as:
// invariant a <= b
class Swap private (val a: Int, val b: Int)
object Swap {
def apply(a: Int, b: Int): Swap =
if (a <= b) new Swap(a, b) else new Swap(b, a)
}
Unfortunately you have to use class here and reimplement equals hashCode etc yourself, as we cannot get rid of scalac auto-generated apply: related SO Q/A
And make all functions on Swap maintain that invariant.
Then equals comparion is essentially this.a == other.a && this.b == other.b, we don't need to care about swapping anymore.
The problem is that you don't know statically that a is a Set2 - as far as the compiler is concerned you called Set(as: A*) and got back some kind of Set.
You could use shapeless sized collections to enforce a statically known collection size.

Why can't I flatMap a Try?

Given
val strings = Set("Hi", "there", "friend")
def numberOfCharsDiv2(s: String) = scala.util.Try {
if (s.length % 2 == 0) s.length / 2 else throw new RuntimeException("grr")
}
Why can't I flatMap away the Try resulting from the method call? i.e.
strings.flatMap(numberOfCharsDiv2)
<console>:10: error: type mismatch;
found : scala.util.Try[Int]
required: scala.collection.GenTraversableOnce[?]
strings.flatMap(numberOfCharsDiv2)
or
for {
s <- strings
n <- numberOfCharsDiv2(s)
} yield n
<console>:12: error: type mismatch;
found : scala.util.Try[Int]
required: scala.collection.GenTraversableOnce[?]
n <- numberOfCharsDiv2(s)
However if I use Option instead of Try there's no problem.
def numberOfCharsDiv2(s: String) = if (s.length % 2 == 0)
Some(s.length / 2) else None
strings.flatMap(numberOfCharsDiv2) # => Set(1, 3)
What's the rationale behind not allowing flatMap on Try?
Let's look at the signature of flatMap.
def flatMap[B](f: (A) => GenTraversableOnce[B]): Set[B]
Your numberOfCharsDiv2 is seen as String => Try[Int]. Try is not a subclass of GenTraversableOnce and that is why you get the error. You don't strictly need a function that gives a Set only because you use flatMap on a Set. The function basically has to return any kind of collection.
So why does it work with Option? Option is also not a subclass of GenTraversableOnce, but there exists an implicit conversion inside the Option companion object, that transforms it into a List.
implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList
Then one question remains. Why not have an implicit conversion for Try as well? Because you will probably not get what you want.
flatMap can be seen as a map followed by a flatten.
Imagine you have a List[Option[Int]] like List(Some(1), None, Some(2)). Then flatten will give you List(1,2) of type List[Int].
Now look at an example with Try. List(Success(1), Failure(exception), Success(2)) of type List[Try[Int]].
How will flatten work with the failure now?
Should it disappear like None? Then why not work directly with Option?
Should it be included in the result? Then it would be List(1, exception, 2). The problem here is that the type is List[Any], because you have to find a common super class for Int and Throwable. You lose the type.
These should be reasons why there isn't an implicit conversion. Of course you are free to define one yourself, if you accept the above consequences.
The problem is that in your example, you're not flatmapping over Try. The flatmap you are doing is over Set.
Flatmap over Set takes a Set[A], and a function from A to Set[B]. As Kigyo points out in his comment below this isn't the actual type signature of flatmap on Set in Scala, but the general form of flat map is:
M[A] => (A => M[B]) => M[B]
That is, it takes some higher-kinded type, along with a function that operates on elements of the type in that higher-kinded type, and it gives you back the same higher-kinded type with the mapped elements.
In your case, this means that for each element of your Set, flatmap expects a call to a function that takes a String, and returns a Set of some type B which could be String (or could be anything else).
Your function
numberOfCharsDiv2(s: String)
correctly takes a String, but incorrectly returns a Try, rather then another Set as flatmap requires.
Your code would work if you used 'map', as that allows you to take some structure - in this case Set and run a function over each element transforming it from an A to a B without the function's return type conforming to the enclosing structure i.e. returning a Set
strings.map(numberOfCharsDiv2)
res2: scala.collection.immutable.Set[scala.util.Try[Int]] = Set(Success(1), Failure(java.lang.RuntimeException: grr), Success(3))
It is a Monad in Scala 2.11:
scala> import scala.util._
import scala.util._
scala> val x: Try[String] = Success[String]("abc")
x: scala.util.Try[String] = Success(abc)
scala> val y: Try[String] = Failure[String](new Exception("oops"))
y: scala.util.Try[String] = Failure(java.lang.Exception: oops)
scala> val z = Try(x)
z: scala.util.Try[scala.util.Try[String]] = Success(Success(abc))
scala> val t = Try(y)
t: scala.util.Try[scala.util.Try[String]] = Success(Failure(java.lang.Exception: oops))
scala> z.flatten
res2: scala.util.Try[String] = Success(abc)
scala> t.flatten
res3: scala.util.Try[String] =
Failure(java.lang.UnsupportedOperationException: oops)
Kigyo explains well why Scala does not do this implicitly. To put it simply, Scala does not want to automatically throw away the exception that is preserved in a Try.
Scala does provide a simple way to explicitly translate a Try into an Option though. This is how you can use a Try in a flatmap:
strings.flatMap(numberOfCharsDiv2(_).toOption)

Type equality in the Scala 2.10 Reflection API

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).

Equality between StringOps and WrappedString instances

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].

Ordering and Ordered and comparing Options

Given:
case class Person(name: String)
and trying to do:
scala> List(Person("Tom"), Person("Bob")).sorted
results in a complaint about missing Ordering.
<console>:8: error: could not find implicit value for parameter ord: Ordering[Person]
List(Person("Tom"), Person("Bob")).sorted
However this:
case class Person(name: String) extends Ordered[Person] {
def compare(that: Person) = this.name compare that.name }
works fine as expected:
scala> List(Person("Tom"), Person("Bob")).sorted
res12: List[Person] = List(Person(Bob), Person(Tom))
although there's no Ordering or implicits involved.
Question #1: what's going on here? (My money is on something implicit...)
However, given the above and the fact that this:
scala> Person("Tom") > Person("Bob")
res15: Boolean = true
works, and that also this:
scala> List(Some(2), None, Some(1)).sorted
works out of the box:
res13: List[Option[Int]] = List(None, Some(1), Some(2))
I would expect that this:
scala> Some(2) > Some(1)
would also work, however it does not:
<console>:6: error: value > is not a member of Some[Int]
Some(2) > Some(1)
Question #2: why not, and how can I get it to work?
If you install the slightly-too-magical-for-default-scope bonus implicits, you can compare options like so:
scala> import scala.math.Ordering.Implicits._
import scala.math.Ordering.Implicits._
scala> def cmpSome[T: Ordering](x: Option[T], y: Option[T]) = x < y
cmpSome: [T](x: Option[T], y: Option[T])(implicit evidence$1: Ordering[T])Boolean
The import gives you an implicit from an Ordering to the class with the infix operations, so that it's enough to have the Ordering without another import.
Concerning your first question: Ordered[T] extends Comparable[T]. The Ordering companion object provides an implicit Ordering[T] for any value that can be converted into a Comparable[T]:
implicit def ordered[A <% Comparable[A]]: Ordering[A]
There is no implicit conversion A : Ordering => Ordered[A] - that's why Some(1) > Some(2) will not work.
It is questionable if it is a good idea to define such a conversion as you may end up wrapping your objects into Ordered instances and then create an Ordering of that again (and so on...). Even worse: you could create two Ordered instances with different Ordering instances in scope which is of course not what you want.
The definition of List's sorted method is:
def sorted [B >: A] (implicit ord: Ordering[B]): List[A]
So yes, implicit things are happening, but many classes in the standard library have implicit objects associated with them without you having to import them first.
The Ordering companion object defines a bunch of implicit orderings. Among these is an OptionOrdering and IntOrdering, which helps explain the ability of a list to call sorted.
To gain the ability to use operators when there is an implicit conversion available, you need to import that object, for example:
def cmpSome(l:Option[Int], r:Option[Int])(implicit ord:Ordering[Option[Int]]) = {
import ord._
l < r
}
scala> cmpSome(Some(0), Some(1))
res2: Boolean = true
To answer your second question, why can't you do this: Some(2) > Some(1)
You can, with an import and working with Option[Int] rather than Some[Int].
# import scala.math.Ordering.Implicits._
import scala.math.Ordering.Implicits._
# Some(2) > Some(1) // doesn't work
cmd11.sc:1: value > is not a member of Some[Int]
val res11 = Some(2) > Some(1)
^
Compilation Failed
# (Some(2): Option[Int]) > (Some(1): Option[Int]) // Option[Int] works fine
res11: Boolean = true
# Option(2) > Option(1)
res12: Boolean = true
# (None: Option[Int]) > (Some(1): Option[Int])
res13: Boolean = false
In practise your types will probably be of Option[Int] rather than Some[Int] so it won't be so ugly and you won't need the explicit upcasting.
I assume you understand why sorted does not work when you do not pass in an Ordering and none is available in scope.
As to why the sorted function works when you extend your class from Ordered trait. The answer is that when you extend from Ordered trait, the code type checks as the trait contains function like <,> etc. So there is no need to do implicit conversion and hence no complains about the missing implicit Ordering.
As for your second question, Some(2) > Some(1) will not work because Some does not extend the trait Ordered, neither does there seem to be any implicit function in scope that implicitly converts a Some to something that has the function >
Thanks for a detailed question with examples.
My answer is based what I learnt from a great article here: http://like-a-boss.net/2012/07/30/ordering-and-ordered-in-scala.html
All credit to the author here.
Quoting the article:
Coming back to our Box example - the scala library defines an implicit conversion between Ordered[T] and Ordering[T] and vice-versa.
The companion object of Ordered in https://github.com/scala/scala/blob/2.12.x/src/library/scala/math/Ordered.scala provides the required conversion here:
/** Lens from `Ordering[T]` to `Ordered[T]` */
implicit def orderingToOrdered[T](x: T)(implicit ord: Ordering[T]): Ordered[T] =
new Ordered[T] { def compare(that: T): Int = ord.compare(x, that) }
However the reverse conversion isn't defined and I am not sure why?