6

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?

Michael Lorton
  • 43,060
  • 26
  • 103
  • 144

3 Answers3

4

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] {
chengpohi
  • 14,064
  • 1
  • 24
  • 42
1

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" 
}
Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
0

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

Suma
  • 33,181
  • 16
  • 123
  • 191