Specifically:
scala> def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
}
f: (n: Seq[Any])String
scala> f(Stream())
res1: String = Empty
scala> f(List(1))
res17: String = Non-empty
scala> f(Stream(1))
scala.MatchError: Stream(1, ?) (of class scala.collection.immutable.Stream$Cons)
at .f(<console>:13)
... 33 elided
There are a lot of other ways to implement this, but at written the code was statically safe and failed at run time. What's going on?
For Stream, the concat symbol should be #::, the pattern match should like:
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
case h #:: t => "Non-empty stream"
}
for :: is for List / Seq type(List extends from Seq:) ), see:
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
You can only use :: to deconstruct a List and not a Stream. Since the Stream you provide does not match a List, you get a MatchError. If you want f to support streams using that type of matching (extractors), you can use #::.
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case h :: t => "Non-empty"
case h #:: t => "Non-empty"
}
In general, this approach is very fragile because both extractor types shown above will only work for those two types of Seq. Others maybe break. If all you care about is determining whether or not the Seq is empty or not, then simply use n.nonEmpty or n.isEmpty and deal with the Boolean result. Otherwise, trying to provide an exhaustive match on a trait that is not sealed is bound to fail.
You can also use the Seq extractor:
def f(n: Seq[Any]) = n match {
case Nil => "Empty"
case Seq(_*) => "Non-empty"
}
While other answers show correctly an extractor specific for Stream, which is #::, there exists an extractor for Seq (and anything derived from SeqLike) - it is +:, this works for both List (as ::) and Stream (as `#::). With this extractor you can easily write a function which works with both:
def f(n: Seq[Any]) = n match {
case h +: t => "Non-empty"
case _ => "Empty"
}
See also Scala pattern matching on sequences other than Lists
Related
Consider this code:
def f1(a: Int, b: Int) = a + b
def f2(a: Option[Int], b: Option[Int]): Int = (a, b) match {
case (Some(x), Some(y)) => x + y
case _ => throw new IllegalArgumentException
}
println(f1(10, 20))
println(f2(Some(10), Some(20)))
I heard you can "lift" f1 to be like f2. As a beginner I have the following question:
What is lifting and why it used? In terms of implementation, how can I "lift" f1?
Any explanation is greatly appreciated as it is a bit tricky to find why I would "lift" something
Why: when you have a function with signature like f1 and want to "call it" on Option[Int]s (or List[Int]s, etc)
How: you could write it directly:
def lift2option[A, B, C](f: (A, B) => C): (Option[A], Option[B]) => Option[C] = ???
I'm leaving it undefined because you should try writing it yourself; your definition of f2 should be a good starting point. Note that I made it return Option[Int] instead of Int. Later I can edit and give the answer if you want.
And then instead of defining f2 as separate function, you do:
val f2 = lift2option(f1 _)
println(f2(Some(10), Some(20)))
Of course, the point is that now for any function with a signature like f1 you can get an f2 equivalent.
It can be further generalized to work not only for Option, but you'll want to look into that later.
I just wanted to add that lifting a function can be used as an alternative to mapping over a functor. For example, if you had 2 Option[Int] objects which you wanted to apply f1 to, you could do this:
val sum: Option[Int] = option1.flatMap { x => option2.map{ y => x + y } }
Note that the result is an Option[Int]. As Alexey Romanov said, the return type of f2 should also be an Option. The whole point of Option is to let you do operations on a value without fear of NullPointerExceptions or other errors because the value doesn't exist.
However, this mapping is a bit verbose, and it's annoying having to decide when you need to use flatMap and map. This is where lifting comes in handy.
Let's define f2 a little better to handle Nones:
def f2(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(x + y)
case None => None
}
case None => None
}
We can also define this in terms of f1 by replacing x + y with f1(x + y)
def f2(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
Right now, f2 doesn't need to know anything about how to add numbers, it just uses f1 to add them. We could even make f1 a parameter of f2, in fact.
def f2(f1: (Int, Int) => Int)(a: Option[Int], b: Option[Int]): Option[Int] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
See what happened there? We just used f2 to "lift" f1 from (Int, Int) => Int to (Option[Int], Option[Int]) => Option[Int]. Let's rename it lift2, actually. We could also make it more generic:
def lift2[A, B, C](f1: (A, B) => C)(a: Option[A], b: Option[B]): Option[C] =
a match {
case Some(x) => b match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
lift2 is now a function that takes a function of type (A, B) => C (here, A, B, and C are all Int for f1) and returns another function of type (Option[A], Option[B]) => Option[C]. Now, we don't have to use those awkward nested maps and flatMaps. You can just do this:
val sum: Option[Int] = lift2(f1)(option1, option2)
You can also define lift3, lift4, etc., of course, but it's probably easier to define just a lift1 function and use currying to do the rest.
Of course, you can only lift a function if you know how to take apart and put together the type that you are lifting to. For example, if Some were an object with a private unapply method, and it were impossible to pattern match on it, you wouldn't be able to lift f1. The same would happen if the constructor for Some were private and it were impossible for you to make new Options.
Edit: Here's how you can add multiple Option[Int] objects together with f1 and the lift2 function.
val f2 = lift2(f1)
val optionSum = f2(f2(option1, option2), option3)
Without f2, it'd look something like this
val sum1 = option1 match {
case Some(x) => option2 match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
val finalSum = sum1 match {
case Some(x) => option3 match {
case Some(y) => Some(f1(x, y))
case None => None
}
case None => None
}
I have the following snippet of scala code:
def append(as: List[Int], bs: List[Int]): List[Int] = as match {
case Nil => bs
case x::xs => x::append(xs, bs)
}
I don't understand the x:: in the line where x::append(xs, bs) is. I know that when the list as is empty then bs will be returend (with an appended as when as was not empty before). But how does scala know in the mentioned line that is should append as to bs with x::(..,..)
Perhaps it would help if we desugar append a bit
def append(as: List[Int], bs: List[Int]): List[Int] =
as match {
case Nil => bs
case ::(x, xs) =>
val list = append(xs, bs)
list.::(x)
}
Note how :: appears twice, however the first one in
case ::(x, xs)
is actually a case class, even though its symbolic name seems strange, and its declaration looks a bit like this
case class :: (head: A, next: List[A])
whilst, on the other hand, the second :: in
list.::(x)
is actually a right-associative method which Adds an element at the beginning of this list and looks something like so
def :: (elem: B): List[B]
Note how x :: list is equivalent to list.::(x) and not x.::(list) meaning :: is invoked on the right argument.
I have a list of elements, and want to group them by class and then process the results.
trait H
case class A(x: Int) extends H
case class B(x: Int) extends H
val x = List(A(1),B(2),B(3))
val y = x.groupBy(_.getClass)
y.map(_ match {case (A, alist) => println("found me some As")
case (B, blist) => println("not As")})
Unfortunately this produces errors like:
<console>:17: error: pattern type is incompatible with expected type;
found : A.type
required: Class[_ <: Product]
Note: if you intended to match against the class, try `case _: A`
y.map(_ match {case (A, alist) => println("found me some As")
That is, I can't seem to find the proper way to do case matching when the item being matched is a class not just an instance of one.
A partial solution is the following:
val z = y.map(_ match {case (atype, alist) => alist })
z.map(_ match {
case alist if alist.head.isInstanceOf[A] => alist
case blist => List()
})
But it feels like there should be a better way of doing this using the keys to the initial Map returned from groupBy.
A in case (A, alist) refers to the companion object of A, which has the type A.type. That's why you're getting that error message. If you want to match against the class meta data, you should refer to classOf[A]. There's also no reason to use match, as you can pattern match with map already.
y.map {
case (c, alist) if(c == classOf[A]) => println("found me some As")
case (c, blist) if(c == classOf[B]) => println("not As")
}
Making my first steps in Scala, I have run into the first misunderstanding.
I took a classic example of a linked list.
sealed trait List[+A] // `List` data type, parameterized on a type, `A`
case object Nil extends List[Nothing] // A `List` data constructor representing the empty list
/* Another data constructor, representing nonempty lists. Note that `tail` is another `List[A]`,
which may be `Nil` or another `Cons`.
*/
case class Cons[+A](head: A, tail: List[A]) extends List[A]
object List { // `List` companion object. Contains functions for creating and working with lists.
def sum(ints: List[Int]): Int = ints match { // A function that uses pattern matching to add up a list of integers
case Nil => 0 // The sum of the empty list is 0.
case Cons(x,xs) => x + sum(xs) // The sum of a list starting with `x` is `x` plus the sum of the rest of the list.
}
def product(ds: List[Double]): Double = ds match {
case Nil => 1.0
case Cons(0.0, _) => 0.0
case Cons(x,xs) => x * product(xs)
}
def apply[A](as: A*): List[A] = // Variadic function syntax
if (as.isEmpty) Nil
else Cons(as.head, apply(as.tail: _*))
val x = List(1,2,3,4,5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + sum(t)
case _ => 101
}
}
There are a few questions:
1) why custom linked List class doesn't conflict with built-in scala.collection.immutable.List.type
2) why a piece of code is supposed to be correct when we are matching built-in List to the custom linked list?
val x = List(1,2,3,4,5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + sum(t)
case _ => 101
}
The custom linked-list class doesn't conflicts with the built-in scala.collection.immutable.List.type because local declarations, such as your custom List type, has higher precedence than an import (even non-explicit ones such as Scala's built-in List). See Chapter 2 of the Scala Specification for the full precedence order.
The referred matching code is not matching the built-in List, but your own locally declared List. You can see it yourself, by renaming your List to something like CustomList and see that some errors will appear, or to fully qualify the built-in List as the following code.
The following code actually matches the built-in List with your custom List structures and won't compile:
val x = scala.collection.immutable.List(1,2,3,4,5) match {
case Cons(x, Cons(2, Cons(4, _))) => x
case Nil => 42
case Cons(x, Cons(y, Cons(3, Cons(4, _)))) => x + y
case Cons(h, t) => h + sum(t)
case _ => 101
}
Your question is really about scope, I believe. You have defined your own List which is unrelated to that in scala.collection.immutable... The same with Cons and Nil.
When you instantiate the List in part 2), you are instantiating your List, not the one in the Scala library.
Or am I missing something?
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 // :)