2

I'm trying to use a for expression to map over an Option, but I only want to match if the contents of the Option are of a specific type. What I thought would work is this:

for {
  vcs: Mercurial <- maybeVcs
} yield vcs

But that yields the following compile error:

<console>:76: error: type mismatch;
 found   : sbtrelease.Mercurial => sbtrelease.Mercurial
 required: sbtrelease.Vcs => ?
                vcs: Mercurial <- get (releaseVcs in Compile)
                               ^

Is it possible to pattern match on type in a for expression?

mfirry
  • 3,634
  • 1
  • 26
  • 36
gregsymons
  • 317
  • 2
  • 11
  • 1
    Possible duplicate of [Why scala's pattern maching does not work in for loops for type matching?](http://stackoverflow.com/questions/11394034/why-scalas-pattern-maching-does-not-work-in-for-loops-for-type-matching) – Dominique Unruh Aug 11 '16 at 12:39
  • Does this answer your question? [Why does pattern matching in Scala not work with variables?](https://stackoverflow.com/questions/7078022/why-does-pattern-matching-in-scala-not-work-with-variables) – Suma Aug 17 '22 at 13:46

3 Answers3

2

It's really straightforward if you use collect instead of for:

trait A
case class B(x: Int) extends A
case class C(y: Int) extends A

val someB: Option[A] = Some(B(2))
val someC: Option[A] = Some(C(2))
val noneA: Option[A] = None
someB.collect { case n: B => n }   // Some(B(2))
someC.collect { case n: B => n }   // None
noneA.collect { case n: B => n }   // None
dhg
  • 52,383
  • 8
  • 123
  • 144
  • That's what I wound up doing, but the for comprehension feels cleaner to me (at least if it worked:), since a pattern match for a single case seems awkward. – gregsymons Nov 09 '15 at 16:56
1

The fact that this pattern match does not work is actually a bug (at least its not in accordance with the spec). See https://issues.scala-lang.org/browse/SI-900.

However, there is a simple workaround. Define somewhere the following object:

object Id { def unapply[T](x:T) = Some(x) }

Now you can use Id(x) as a pattern match that matches everything, and just binds x to whatever it matched. So basically a pointless construct, since Id(pattern) is the same as pattern.

However, it has one effect: A type annotation inside Id(...) will not be interpreted as a type annotation, but as a type pattern. Thus

for {
  Id(vcs: Mercurial) <- maybeVcs
} yield vcs

will have the effect you desire. (And differently from Bob's answer, the overall expression will have type Seq[Mercurial] and not Seq[Vcs].)

Suma
  • 33,181
  • 16
  • 123
  • 191
Dominique Unruh
  • 1,248
  • 8
  • 23
0

You can use an ugly test:

for {
  vcs <- maybeVcs
  if vcs.instanceof[Mercurial]
} yield vcs
Bob Dalgleish
  • 8,167
  • 4
  • 32
  • 42