Scala: Pattern matching Seq[Nothing] - scala

I am trying to match the case where a Seq contains Nothing.
models.Tasks.myTasks(idUser.toInt) match {
case tasks => tasks.map {
task => /* code here */
}
case _ => "" //matches Seq(models.Tasks)
}
How is Seq[Nothing] represented in pattern matching ?

Matching against an empty sequence looks like this:
val x: Seq[Nothing] = Vector()
x match {
case Seq() => println("empty sequence")
}
EDIT: Note that this is more general than case Nil since Nil is a subclass only of List, not Seq in general. Strangely, the compiler is ok with matching against Nil if the type is explicitly annotated as Seq, but it will complain if the type is any non-List subclass of Seq. Thus you can do this:
(Vector(): Seq[Int]) match { case Nil => "match" case _ => "no" }
but not this (fails with compile-time error):
Vector() match { case Nil => "match" case _ => "no" }

Assuming I understand what you mean correctly, a sequence that contains nothing is empty, which is Nil:
case Nil => //do thing for empty seq
This works even though you're dealing with Seqs, not Lists:
scala> Seq()
res0: Seq[Nothing] = List()
scala> Seq() == Nil
res1: Boolean = true
Some more REPL output to show that this works absolutely fine with other subclasses of Seq:
scala> Nil
res3: scala.collection.immutable.Nil.type = List()
scala> val x: Seq[Int] = Vector()
x: Seq[Int] = Vector()
scala> x == Nil
res4: Boolean = true
scala> x match { case Nil => "it's nil" }
res5: java.lang.String = it's nil
scala> val x: Seq[Int] = Vector(1)
x: Seq[Int] = Vector(1)
scala> x match { case Nil => "it's nil"; case _ => "it's not nil" }
res6: java.lang.String = it's not nil
As can be seen from the above output, Nil is a type all of it's own. This question has some interesting things to say on the matter.
But #dhg is correct that if you manually create a specific subtype such as vector, the match does not work:
scala> val x = Vector()
x: scala.collection.immutable.Vector[Nothing] = Vector()
scala> x match { case Nil => "yes"}
<console>:9: error: pattern type is incompatible with expected type;
found : object Nil
required: scala.collection.immutable.Vector[Nothing]
x match { case Nil => "yes"}
Having said that, I don't know why you would need to force your objects to be a referred to as a specific concrete subclass very often.

Related

find the data type in List

I have a requirement where I need to perform some set of transformations based on the datatype of List. Lets say if I get List[String] I need to apply some transformations but if I get a List[Int] some different transformations need to be applied. I had defined a function which will take List[Any] and with match statement I need to check the datatype. I tried to use isInstanceOf but it didn't work out.
How can I check the datatype of List.
Assuming that your list have same type in all of its elements and using plain Scala you could do something like this:
def test(list: List[Any]): List[Any] = {
if(list.isEmpty) return List()
list.head match {
case a: String => list.map(str => str.asInstanceOf[String]+"3")
case a: Int => list.map(int => int.asInstanceOf[Int]+3)
}
}
It's not the best solution, but i don't see anything else which you could use without different libraries
Here is even weirder solution, which allows you to return exactly same type which you've put to this function, of course you would need have exact same type in every element of list:
def test[T](list: List[T]): List[T] = {
if(list.isEmpty) return List()
list.head match {
case a: String => list.map(str => (str.asInstanceOf[String]+"3").asInstanceOf[T])
case a: Int => list.map(int => (int.asInstanceOf[Int]+3).asInstanceOf[T])
}
}
test(List("123","123")) // res0: List[String] = List(1233, 1233)
test(List(1,2,3)) // res1: List[Int] = List(4, 5, 6)
And again edit, last but not least, you can use TypeTag to avoid type erasure and check list type, like this:
def test1[T: TypeTag](list: List[T]): List[T] = typeOf[T] match {
case t if t =:= typeOf[String] => list.map(str => (str.asInstanceOf[String]+"3").asInstanceOf[T])
case t if t =:= typeOf[Int] => list.map(int => (int.asInstanceOf[Int]+3).asInstanceOf[T])
case _ => list
}
test1(List("123", "123", "123")) // Strings so: res0: List[String] = List(1233, 1233, 1233)
test1(List("123","123", 1)) // Oh-oh we have not defined type, so: res1: List[Any] = List(123, 123, 1)
test1(List(1,2,3)) // And here we have res2: List[Int] = List(4, 5, 6)

Any convention for "given an empty list return an empty list" when matching patterns?

When pattern matching a list, it seems common to return an empty list when given an empty list. We can match an empty list to Nil or List(), but we can return empty as Nil, List() or by returning the given list argument itself.
What's the convention here?
When would you choose one method over another?
Examples:
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case List() => Nil
case x :: xs => ???
}
def givenEmptyNumsReturnsEmptyList(nums: List[Int]): List[Int] = nums match {
case List() => List()
case x :: xs => ???
}
def givenEmptyNumsReturnsNums(nums: List[Int]): List[Int] = nums match {
case List() => nums
case x :: xs => ???
}
I'm scala beginner and don't know any existing convention about it. My things about it:
The last one isn't intuitive
I prefer return that I match. If I have case List(), so I return List().
You can also match Nil:
-
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case Nil => Nil
case x :: xs => ???
}
But Nil and List() are the same.
For choice better way, just clarify what inside:
case N1:
def givenEmptyNumsReturnsEmptyList(nums: List[Int]): List[Int] = nums match {
case List() => List()
case x :: xs => ???
}
Will call unaplay method from object List, after will call apply method of object List.
case N2:
def givenEmptyNumsReturnsNil(nums: List[Int]): List[Int] = nums match {
case Nil => Nil
case x :: xs => ???
}
Will compare value before match with object Nil and will return object Nil
And in case of choice I prefer case N2 because it is little bit optimal.

Why no Type Erasure Warning

After reading this excellent question/answer on type erasure in Scala, I tried this code. The Scala compiler did not output a type erasure warning.
scala> val x: List[Int] = List(1,2,3)
x: List[Int] = List(1, 2, 3)
scala> x match {
| case List(x: Int) => println("a")
| case _ => println("false")
| }
false
Why doesn't the above code output the same warning as this code:
scala> List(1,2,3) match {
| case l: List[String] => println("list of strings")
| case _ => println("ok")
| }
<console>:9: warning: fruitless type test: a value of type List[Int] cannot
also be a List[String] (but still might match its erasure)
case l: List[String] => println("list of strings")
^
list of strings
The first case is not just testing type - it's testing by pattern-match that the list has exactly one integer element.

How to prevent this kind of bug - pattern matching and Nil

When you pattern match again Lists, you can use Nil to check for empty list. However, if the underlying type is an Iterable, you can still check for Nil, and it will break for empty Sets, etc... See following REPL session:
scala> val l: Iterable[Int] = List()
l: Iterable[Int] = List()
scala> l match {
| case Nil => 1
| case _ => 2
| }
res0: Int = 1
scala> val l: Iterable[Int] = Set()
l: Iterable[Int] = Set()
scala> l match {
| case Nil => 1
| case _ => 2
| }
res2: Int = 2
Question is - how can I prevent this kind of issue? Obviously, if l is a type List, it's no bug. And if l is of type Set, it won't compile. But what if we have a class that has a list, define a function that pattern matches in this way, and then someone changes the class to take a generic iterable instead? Is this Nil vs. _ pattern match a bad idea in general?
One possibility is to use a guard:
scala> val xs: Iterable[Int] = Set()
xs: Iterable[Int] = Set()
scala> xs match { case xs if xs.isEmpty => 1 case _ => 2 }
res0: Int = 1
Another way to do this is to use an if-else-expression (works best if you only have one or two conditions to check):
scala> if (xs.isEmpty) 1 else 2
res1: Int = 1
Convert the scrutinee to a list to eliminate doubt.
l.toList match {
case Nil => 1
case xs => 2
}
Here is another option (pun intended):
scala> val l: Iterable[Int] = List()
l: Iterable[Int] = List()
scala> l.headOption match { case None => 1; case Some(h) => 2 }
res0: Int = 1
This is useful in cases where you pattern match in order to get the head like in popular List() match { case h :: t => ... } but it's not a list, it's an Iterable and :: will fail.
I added this answer because I thought it's quite common to pattern match on a collection to get a head, otherwise you can just check with xs.isEmpty.

Scala's Implementation of haskell last method

I am trying to do some examples programs in scala to get more familiar with the language, For that I am trying to re-implement some of the built in methods in Haskell, Most of these methods I am sure are also implemented in Scala too, But these are just for my practice. I think I can post some of code snippets (not all of them) to get a better way of doing things and to validate my understanding of scala. So please let me know if this is not the place to do these things.
Here is my scala implementation to get the last element of any list. Is this the right way of doing things, By using Any am I loosing the type of the object containing in the list? Is this how this kind of things implemented in scala?
def getLast(xs: List[Any]): Any = xs match {
case List() => null
case x :: List() => x
case _ :: ys => getLast(ys)
}
Parameterize the type of your function and use "Nil" instead of List() like so:
def getLast[T](xs: List[T]): T = xs match {
case Nil => null.asInstanceOf[T]
case x :: Nil => x
case _ :: ys => getLast(ys)
}
Also, consider making it return an Option type:
def getLast[T](xs: List[T]): Option[T] = xs match {
case Nil => None
case x :: Nil => Some(x)
case _ :: ys => getLast(ys)
}
Usage:
val listOfInts = List(1,2,3)
assert(getLast(listOfInts).isInstanceOf[Int])
val listOfStrings = List("one","two","three")
assert(getLast(listOfStrings).isInstanceOf[String])
Firstly, avoid the null, and especially null.asInstanceOf[T]. Observe the danger with primitives:
scala> null.asInstanceOf[Int]
res19: Int = 0
scala> null.asInstanceOf[Boolean]
res20: Boolean = false
So the signature should either be List[T] => T, whereby last on an empty iterator throws an exception:
def last[T](ts: List[T]): T = ts match {
case Nil => throw new NoSuchElementException
case t :: Nil => t
case t :: ts => last(ts)
}
Or instead: List[T] => Option[T]
def lastOption[T](ts: List[T]): Option[T] = ts match {
case Nil => None
case t :: Nil => Some(t)
case t :: ts => lastOption(ts)
}
def lastOption1[T](ts: List[T]): Option[T] = ts.reverse.headOption
def lastOptionInScala28[T](ts: List[T]): Option[T] = ts.lastOption // :)