Chaining partial functions with match statements - scala

I am trying to factor out common behaviour from long pattern matching statements to DRY my code out. The idea is to chain the common code in partial functions and have that evaluated by the match statement. My ideal is something like
type pf = PartialFunction[Int, String]
// Our common behaviour
val common: pf = {
case 0 ⇒ "zero"
case 1 ⇒ "one"
}
val a = 2
// speculative match statement with extended behaviour
val b = a match (common orElse {
case 2 ⇒ "two"
case 3 ⇒ "three"
case _ ⇒ "None"
})
Unfortunately, the argument to the match statement is apparently not a partial function. Obviously, the problem can be solved using partial functions, the code below doing the trick but I lose out on the conventional pattern match syntax:
val c: String = (common orElse {
case 2 ⇒ "two"
case 3 ⇒ "three"
case _ ⇒ "None"
}: pf)(a)
The example below is on the ugly side of what I am looking for. It compiles but fails with a scala.MatchError if the argument a is not defined in the common partial function.
val d = common(a) orElse (a match {
case 2 ⇒ "two"
case 3 ⇒ "three"
case _ ⇒ "None"
})
I am looking for the following answers:
Why is the argument to the match statement not translated into partial function?
Why does the ugly example above fail?
Is there a clever way to achieve the goal?

Match has not been generalized as for catch, most recently here:
https://github.com/scala/scala/pull/4334
where you can supply a partial function to a catch.
The catch is easy in that it must be a PartialFunction[Throwable, ?], but it's not obvious to me that pattern matches generalize in the same way. At issue are exhaustiveness checks, the expected type of the scrutinee, who knows what.

What's wrong with this...?
type Pfis = PartialFunction[Int, String]
val common: Pfis = {
case 0 ⇒ "zero"
case 1 ⇒ "one"
}
val a = 2
val other: Pfis = {
case 2 ⇒ "two"
case 3 ⇒ "three"
case _ ⇒ "None"
}
val b = ( common orElse other )( a )
// If you feel like using the "conventional match-case"
val f = common orElse other
val c = a match { case x => f( x ) }
Also, there is no such thing as conventional pattern matching syntax... match is a keyword which "can be thought to act" as a right-associative function which means that a match pf is very much same ( not really same... a match pf does not work) as pf.apply( a ) or just pf( a ).
Also, orElse is a used for functional-composition so basically, you compose a new partial-function by orElseing two partitial-functions. Then you call this new parital-function, the same way as you would use any partial-function.
I think, if you understand what orElse does... you will understand the answer to your second point. Well, it fails because you are applying the partial-function common for argument 2 and it is not defined at 2.
Also, one more thing... even if common was defined at a, you are doing something very wrong. Let me explain,
val t = 1
val d = common( t ) orElse ( t match {
case 2 ⇒ "two"
case 3 ⇒ "three"
case _ ⇒ "None"
})
// Now d is same as following,
val k = "one" orElse "None"
// Here, k and d both are actually PartialFunction[Int,Char] = <function1>
// basically, both k and d take an Int i as argument and return the ( character at index i in "one" ) orElse ( character at index i in "None" )
// So,
println( d( 2 ) )
// Will print - e
println( d( 3 ) )
// since "one" does not have index 3, it will return index 3 in "None"
// will print - e
println( d( 4 ) )
// None of "one" and "None" have index 4
// So, you will get - scala.MatchError: 4 (of class java.lang.Integer)

Related

Pattern matching using string interpolation

In the following example using Scala 2.13.3 the 1st pattern matches, but the 2nd does not.
The 3rd pattern again matches, while the 4th does not (note that separator in the 4th match expression is enclosed in backticks, thus referencing the value defined before).
trait A
case object A extends A {
def unapply(a: String): Option[A] = if (a == "my_a") Some(A) else None
}
trait B
case object B extends B {
def unapply(b: String): Option[B] = if (b == "myB") Some(B) else None
}
val match1 = "myB_my_a" match {
case s"${B(b)}_${A(a)}" => Some((a,b))
case _ => None
} // Some((A,B))
val match2 = "my_a_myB" match {
case s"${A(a)}_${B(b)}" => Some((a,b))
case _ => None
} // None
val match3 = "my_a__myB" match {
case s"${A(a)}__${B(b)}" => Some((a,b))
case _ => None
} // Some((A,B))
val separator = "__"
val match4 = s"my_a${separator}myB" match {
case s"${A(a)}${`separator`}${B(b)}" => Some((a,b))
case _ => None
} // None
Why do only the 1st and the 3rd pattern match?
Is there a good matching alternative to the 2nd pattern that a) is using the unapply methods of A and B and where b) we don't know what strings these methods are accepting?
Edit 1: Added case object B and another matching example.
Edit 2: Another example to illustrate jwvh's answer:
val (a, b) = ("my_a", "myB")
val match5 = s"${a}_${b}" match {
case s"${`a`}_${`b`}" => Some((a, b)) // does not match
case s"${x}_${y}" => Some((x, y)) // matches: Some(("my", "a_myB"))
}
Edit 3: To illustrate how, unlike case class construction and extraction with apply and unapply, the construction and extraction of strings using similar string interpolation are not (and cannot be) inverse functions:
case class AB(a: String, b: String)
val id = (AB.apply _ tupled) andThen AB.unapply andThen (_.get)
val compare = id(("my_a", "myB")) == ("my_a", "myB") // true
val construct: (String, String) => String = (a,b) => s"${a}_${b}"
val extract: String => (String, String) = { case s"${a}_${b}" => (a,b) }
val id2 = (construct tupled) andThen extract
val compare2 = id2(("my_a","myB")) == ("my_a","myB") // false
As your own test (mentioned in the comments) demonstrates, the interpolator recognizes that the match pattern "${A(a)}_${B(b)}" is made up of 2 parts separated by an underscore _. So a best-guess effort is made to split the target string accordingly.
The 1st part, "my", is sent to the A.unapply() where it fails. The 2nd part, "a_myB", is not even attempted.
Something similar happens in match4. The pattern "${A(a)}${'separator'}${B(b)}" has 3 dollar signs and thus 3 parts. But, without any explicit characters to anchor the pattern, the target string is split into these 3 parts.
""
""
"my_a__myB"
Again, the 1st part fails the unapply() and the other parts are never attempted.
While your Edit 3 code is technically correct, I don't find it terribly convincing. You've simply demonstrated that (String,String)=>AB(String,String)=>(String,String) is (or can be) a lossless data transition. The same cannot be said of (String,String)=>String which introduces some ambiguity, i.e. the loss of information sufficient to guarantee restoration of the original data. That loss is inherent in the transformation itself, not the tools (interpolation) used to achieve it.
The fact that case class and String interpolation both use apply()/unapply() under the hood strikes me as inconsequential.

Get value of an optional class field

Let's suppose I have a class Toto with two optional fields :
case class Toto(a : Option[Int], b: Option[Int])
And a class Titi with one optional Toto :
case class Titi(c : Option[Toto])
We create an instance of a class Titi :
val test = Titi(Some(Toto(Some(1),Some(2))))
Now I want to access the second field of Toto in Titi variable by supposing that Titi or b can be equal to None but this statement is impossible :
test.c.getOrElse("Something").b.getOrElse(0)
How do I proceed to do so ?
You should use flatMap:
test.c.flatMap(_.b).getOrElse(0)
In a case in any place in the hierarchy there is None 0 will be returned.
If you have even a deeper object hierarchy with properties returning Option you can chain flatMap:
test.c
.flatMap(_.b)
.flatMap(_.d)
//...etc
Scala also has also the special syntax for unwrapping deeply nested monadic types, called for comprehension:
val result = for {
c <- test.c
a <- c.a
} yield a
result.getOrElse(0)
Under the hood, it is compiled to similar code as chained flatMap.
This basically works like this:
If c is None to gets directly to getOrElse and returns 0
If it's Some then it checks b, if it's None it goes to getOrElse if not then value wrapped in Some is returned.
In case you would want to return something different distinguishing which Option is None, then I would just use match:
test.c match {
case Some(c) => c.getOrElse(0)
// if you return String in one branch and integer in other then inferred type would be Any!
case None => "Something"
}
Your val test is wrong, it should be this
val test = Titi(Some(Toto(Some(1),Some(2))))
Other thing, in the getOrElse, you have to put a type that makes sense
test.c.getOrElse(Toto(None,None))
you can achieve it by pattern matching
val test: Titi = Titi(Some(Toto(Some(1), None)))
val res = test.c match {
case Some(Toto(_, Some(x))) => x
case _ => 0
}
result:
0
val x = test match {
case Titi(x) => {
x match {
case Some(x) => {
x.b match {
case Some(z) => z
case None => 1
}
}
case None => 1
}
}
case _ => 1
} //> x : Int = 2
u can use fold
test.c.fold(0)(_.b.fold(0)(i => i))

scala: anonymous partial function strange syntax

I came across a code similar to this, and it was surprised that it even compiles:
scala> val halfSize: PartialFunction[String, Int] = _.length match {
case even if even % 2 == 0 => even / 2
}
halfSize: PartialFunction[String,Int] = <function1>
scala> List("x", "xx").collect(halfSize)
res1: List[Int] = List(1)
As far as I known, the valid syntax to define a PartialFunction is a case function:
val halfSize: PartialFunction[String, Int] = {
case s if s.length % 2 == 0 => s.length / 2
}
The first code seems more optimized since it calls length only once. But even in the SLS I was not able to find the explanation of the syntax. Is this an undocumented feature of scalac ?
The rules are given in https://www.scala-lang.org/files/archive/spec/2.12/06-expressions.html#anonymous-functions:
The eventual run-time value of an anonymous function is determined by the expected type:
...
PartialFunction[T, U], if the function literal is of the shape x => x match { … }
In this case the literal does have such a shape, as rogue-one's answer explains, so PartialFunction is allowed as the expected type.
(EDIT: actually, it doesn't, since it matches x.length instead of x. This looks like a minor bug, but one which should be fixed by changing the specification.)
A PartialFunction's value receives an additional isDefinedAt member, which is derived from the pattern match in the function literal, with each case's body being replaced by true, and an added default (if none was given) that evaluates to false.
So in this case it ends up with
def isDefinedAt(x: String) = x.length match {
case even if even % 2 == 0 => true
case _ => false
}
val halfSize: PartialFunction[String, Int] = _.length match {
case even if even % 2 == 0 => even / 2
}
The underscore (_) in the above function is just a short hand notation that refers to the single argument of the function. Thus the above snippet is just a short form of
val halfSize: PartialFunction[String, Int] = { (x: String) => x.length match {
case even if even % 2 == 0 => even / 2
}
}

Matching a sequence within a sequence in scala

I'm looking to match a sequence within a sequence like either in ex 1 or ex 2;
List(1, 2, 3, 4) match {
case 1 :: List(_*) :: 4 :: tail => // Ex 1
case 1 :: (seq : List[Int]) :: 4 :: tail => // Ex 2
case _ =>
}
This is a variant to the fixed length sequence pattern _*. Like the _*, I don't care about the content of the inner pattern, but it is important that the length can vary and that the pattern is surrounded by a prefix (such as the 1 above) and a suffix (like the 4).
My question is if any of you have a trick to do this by some crafty unapply magic or if you'd just iterate the list to search for the sequence manually.
Thanks in advance! :-)
This is really outside the scope of what pattern matching is supposed to be used for. Even if you could finagle a set of custom unapply methods to do what you wanted, it wouldn't be apparent whether matching was greedy or not, etc..
However, if you really want to, you can proceed as follows (for example):
import scala.collection.SeqLike
class Decon[A](a0: A, a1: A) {
def unapply[C <: SeqLike[A, C]](xs: C with SeqLike[A, C]): Option[(C, C)] = {
xs.span(_ != a1) match {
case (a0 +: pre, a1 +: post) => Some((pre,post))
case _ => None
}
}
}
val Dc = new Decon(1,4)
scala> List(1,2,3,4,5) match { case Dc(pre, post) => (pre, post); case _ => (Nil, Nil) }
res1: (List[Int], List[Int]) = (List(2, 3),List(5))
Separating the specification of the fixed elements 1 and 4 from the match is necessary; otherwise the normal algorithm would ask the unapply to return values without any knowledge of what was being sought, and then would test to make sure they were correct.
You could do something like this:
List(1,2,3,4) match {
case 1 :: tail if tail.last == 4 => println("Found it!")
}
But if you check the implementation of last in LinearSeqOptimized:
def last: A = {
if (isEmpty) throw new NoSuchElementException
var these = this
var nx = these.tail
while (!nx.isEmpty) {
these = nx
nx = nx.tail
}
these.head
}
It is iterating indeed. This is because List inherits from LinearSeq which are optimized to provide efficient head and tail operations. For what you want to do, it is better to use an IndexedSeq implementation like Vector which has an optimized length operation, used in last:
override /*TraversableLike*/ def last: A = {
if (isEmpty) throw new UnsupportedOperationException("empty.last")
apply(length-1)
}
So you could do something like this:
Vector(1,2,3,4) match {
case v if v.head == 1 && v.last == 4 => println("Found it!")
}

How is a match word omitted in Scala?

In Scala, you can do
list.filter { item =>
item match {
case Some(foo) => foo.bar > 0
}
}
But you can also do the quicker way by omitting match:
list.filter {
case Some(foo) => foo.bar > 0
}
How is this supported in Scala? Is this new in 2.9? I have been looking for it, and I can figure out what makes this possible. Is it just part of the Scala compiler?
Edit: parts of this answer are wrong; please refer to huynhjl's answer.
If you omit the match, you signal the compiler that you are defining a partial function. A partial function is a function that is not defined for every input value. For instance, your filter function is only defined for values of type Some[A] (for your custom type A).
PartialFunctions throw a MatchError when you try to apply them where they are not defined. Therefore, you should make sure, when you pass a PartialFunction where a regular Function is defined, that your partial function will never be called with an unhanded argument. Such a mechanism is very useful e.g. for unpacking tuples in a collection:
val tupleSeq: Seq[(Int, Int)] = // ...
val sums = tupleSeq.map { case (i1, i2) => i1 + i2 }
APIs which ask for a partial function, like the collect filter-like operation on collections, usually call isDefinedAt before applying the partial function. There, it is safe (and often wanted) to have a partial function that is not defined for every input value.
So you see that although the syntax is close to that of a match, it is actually quite a different thing we're dealing with.
The language specification addresses that in section 8.5. The relevant portions:
An anonymous function can be defined by a sequence of cases
{ case p1 => b1 ... case pn => bn }
If the expected type is scala.Functionk[S1, ..., Sk, R] , the expression is taken to
be equivalent to the anonymous function:
(x1 : S1, ..., xk : Sk) => (x1, ..., xk) match {
case p1 => b1 ... case pn => bn
}
If the expected type is scala.PartialFunction[S, R], the expression is taken to
be equivalent to the following instance creation expression:
new scala.PartialFunction[S, T ] {
def apply(x: S): T = x match {
case p1 => b1 ... case pn => bn
}
def isDefinedAt(x: S): Boolean = {
case p1 => true ... case pn => true
case _ => false
}
}
So typing the expression as PartialFunction or a Function influences how the expression is compiled.
Also trait PartialFunction [-A, +B] extends (A) ⇒ B so a partial function PartialFunction[A,B] is also a Function[A,B].
-- Revised post --
Hmm, I'm not sure I see a difference, Scala 2.9.1.RC3,
val f: PartialFunction[Int, Int] = { case 2 => 3 }
f.isDefinedAt(1) // evaluates to false
f.isDefinedAt(2) // evaluates to true
f(1) // match error
val g: PartialFunction[Int, Int] = x => x match { case 2 => 3 }
g.isDefinedAt(1) // evaluates to false
g.isDefinedAt(2) // evaluates to true
g(1) // match error
It seems f and g behave exactly the same as PartialFunctions.
Here's another example demonstrating the equivalence:
Seq(1, "a").collect(x => x match { case s: String => s }) // evaluates to Seq(a)
Even more interesting:
// this compiles
val g: PartialFunction[Int, Int] = (x: Int) => {x match { case 2 => 3 }}
// this fails; found Function[Int, Int], required PartialFunction[Int, Int]
val g: PartialFunction[Int, Int] = (x: Int) => {(); x match { case 2 => 3 }}
So there's some special casing at the compiler level to convert between x => x match {...} and just {...}.
Update. After reading the language spec, this seems like a bug to me. I filed SI-4940 in the bug tracker.