2
votes

Reading this, I still have questions about unapply() that returns Boolean.

If take a look at Scala Programming Book (2nd edition), page 602. There is an example:

case Email(Twice(x @ UpperCase()), domain) => ...

Where UpperCase defined as an object that has unapply() returning Boolean and does not have apply()

object UpperCase {
 def unapply(s:String): Boolean = s.toUpperCase == s
}

Where Twice is something like this:

object Twice {
 def apply(s:String): String = s + s
 ...

The questions are (must be too many, sorry for that):

How does UpperCase().unapply(..) works here?

If I pass: [email protected], then x in first code snippet = 'DI'.. then we use '@' ..to bind 'x' to pass it to UpperCase.unapply to invoke unapply(x) i.e unapply('DIDI') (?) Then it returns True.

But why not Option ? I tend to think that unapply returns Option.. kind of one way how it works. That's probably because usually Option wraps some data, but for simple case we should NOT wrap boolean? And because we do not have apply()?

What the difference, when use Boolean / Option ? Based on this example.

And this syntax: x @ UpperCase(), is it supposed to substitute value match case (is that way how I suppose to read it?) syntax if we are matching inside one particular case? It doesn't seems as unified way/syntax of doing this.

Usually syntax like (assuming that x,y is Int): case AA(x @ myX, y) => println("myX: " + myX) says that x binds to myX, basically myX is alias to x.. in this case. But in our case - x @ UpperCase(). x binds to UpperCase().unapply().. putting x as parameter. I mean binding here is quite abstract/wide notion..

2

2 Answers

6
votes

This is simple:

1) If you return Boolean, your unapply just tests matching query

scala> object UpperCase {
     | def unapply(s: String) = s.toUpperCase == s
     | }
defined module UpperCase

scala> "foo" match {
     | case UpperCase() => true
     | case _ => false
     | }
res9: Boolean = false

2) If you return Option[T], you create an extractor, which unwraps T

scala> object IsUpperCase {
     | def unapply(s: String) = Option(s).map(x => x.toUpperCase == x)
     | }
defined module IsUpperCase

scala> "FOO" match {case IsUpperCase(flag) => flag}
res0: Boolean = true
2
votes

This is not so simple.

The behavior of "boolean test" match just changed in the latest milestone:

apm@mara:~/clones/scala$ ./build/pack/bin/scala
Welcome to Scala version 2.11.0-20130911-042842-a49b4b6375 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> 

scala> object OK { def unapply(s: String) = Some(s) filter (_ == "OK") }
defined object OK

scala> import PartialFunction._
import PartialFunction._

scala> cond("OK") { case OK() => true }
<console>:12: error: wrong number of patterns for object OK offering String: expected 1, found 0
              cond("OK") { case OK() => true }
                                ^
<console>:12: error: wrong number of patterns for object OK offering String: expected 1, found 0
              cond("OK") { case OK() => true }
                                  ^

scala> cond("OK") { case OK(x) => true }
res1: Boolean = true

scala> :q

Previously, you could use the extractor without extracting any fields, just for a "boolean test", even though the result of the unapply is not Boolean:

apm@mara:~/clones/scala$ scalam
Welcome to Scala version 2.11.0-M4 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.

scala> import PartialFunction._
import PartialFunction._

scala> object OK { def unapply(s: String) = Some(s) filter (_ == "OK") }
defined object OK

scala> cond("OK") { case OK() => true }
res0: Boolean = true

scala> cond("OK") { case OK(x) => true }
res1: Boolean = true

Here is some discussion and here is the change in the way extractor signatures are processed.