I've been working with Scala for a while and it still troubles me a lot. I don't know why they made it so complex. I am trying to understand matching case classes when there are only two members for this case class
def main(args: Array[String]): Unit = {
case class X(a: String, i: Int)
def doSome(x: X): Unit = {
x match {
case "x" X 1 => print("ahhh") // <---- HERE !
case X(_, _) => println("")
}
}
doSome(X("x", 1))
case class Y(a: String, i: Int, j: Int)
def doAnother(y:Y): Unit = {
y match {
case "y" X 1 => print("ahhh") // how to make similar syntax when there are more than one syntax ?
case Y(_, _,_) => println("") // this is understandable
}
}
doAnother(Y("y", 1,2))
}
How can the syntax "x" X 1 match X("x",1) and if "x" X 1 can match match X("x",1) then what matches Y("y",1,2), obviously "y" Y 1 Y 2 doesn't work?
What is so special about the first argument if we can match on "y" Y (1,2)?
At least in case of List it feels more natural to me, for example consider
List(42, 11) match {
case head :: tail =>
case Nil =>
}
as opposed to
List(42, 11) match {
case ::(head, tail) =>
case Nil =>
}
where head :: tail communicates directly the shape of the List.
As a side note, infix notation can sometimes communicate intent more clearly, for example, consider syntax of generalised constraints
implicitly[List[Int] <:< Iterable[Int]] // infix type notation seems more natural
implicitly[<:<[List[Int], Iterable[Int]]]
You don't have to use a language feature just because it is there.
In this case I can see no reason not to use the standard class matching version:
x match {
case X("x", 1) => print("ahhh")
case _ => println("")
}
}
y match {
case Y("y", 1, _) => print("ahhh")
case _ => println("")
}
Ok, so the thing I was looking for is called "Infix Types". From Scala for Impatient, 2nd edition
An infix type is a type with two type parameters, written in “infix”
syntax, with the type name between the type parameters. For example,
you can write String Map Int instead of Map[String, Int] The
infix notation is common in mathematics. For example, A × B = { (a, b)
| a Œ A, b Œ B } is the set of pairs with components of types A and B.
In Scala, this type is written as (A, B). If you prefer the
mathematical notation, you can define type ×[A, B] = (A, B) Then you
can write String × Int instead of (String, Int). All infix type
operators have the same precedence. As with regular operators, they
are left-associative unless their names end in :. For example,
String × Int × Int means ((String, Int), Int). This type is similar
to, but not the same, as (String, Int, Int), which could not be
written in infix form in Scala.
To answer your query on What is so special about the first argument if we can match on "y" Y (1,2)?: this is because of how your case class gets decomposed via its unapply method.
The preferred way of matching against a case class is how you've done in the second statement of both your methods.
However, for Y, the preferred way to match would be case Y("y", 1, 2) as mentioned in Tim's comment.
For X, a few ways you can use power of pattern matching are (similarly for Y):
case X("x", 1) => ???
case X(a, 1) => ???
case X(_, 1) => ???
case X("x", _) => ???
case x#X("x", _) =>
case X(a, b) if b > 5 => ???
The following, however, is a very bad style as it compromises readability and hence maintainability of the code
case "x" X 1 => print("ahhh")
As Mario mentioned, the way you're using pattern matching is more suited for lists instead of case classes, as it makes your code consistent with the way a list is structured (head cons tail), and thus enhances readability.
You can go through following articles for a more deeper understanding on how to exploit the power of scala's pattern matching:
https://www.artima.com/pins1ed/case-classes-and-pattern-matching.html
https://docs.scala-lang.org/tour/pattern-matching.html
https://docs.scala-lang.org/overviews/scala-book/match-expressions.html
Related
In my scenario, there are three types: A, B and C with C being a subtype of A.
class C extends A
val a: Array[A] = ...
val b: Array[B] = ...
I've been trying to zip a and b first and then filter the results based on the type of the first element, but it seems I'm missing something here.
a.zip(b).filter(_: (A, B) => _._1.isInstanceOf[C]
This does not seem to work, since Scala completely disregards my type declaration for _ on the left side to type _ on the right side as Array[(A, B)].
Since I know isInstanceOf is not the scala way of doing things, I also considered using collect like this:
a.zip(b).collect{case x: (C, B) => x}
But this does not work, since scala does expect the types (A, B) here, rendering the type matching useless.
I'm used to Python programming, so maybe my approach is off here. Would appreciate any pointer in the right direction.
_ is an anonymous parameter, you don't "declare" it, so it's either this:
a.zip(b).filter(_._1.isInstanceOf[C])
or this:
a.zip(b).filter { x: (A, B) => x._1.isInstanceOf[C] }
(note curly braces too)
Can also write it with a partial function syntax:
a.zip(b).filter { case (a,b) => a.isInstanceOf[C] }
or (better):
a.zip(b).filter {
case (_: C, _) => true
case _ => false
}
or use collect as you suggested:
a.zip(b).collect { case x#(_: C, _) => x }
Personally, I prefer the last option over others.
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.
What would be the best and/or easiest way to extract a value that I've saved in a case class?
take for example the following code:
abstract class Something
case class Foo(input: Int) extends Something
case class Bar(input: Double) extends Something
def someMethod(a: Something, b: Something) {
// code that extracts values goes here
}
someMethod(Foo(10), Foo(20))
someMethod(Bar(2.1), Bar(21.2))
how would I then go about getting the integer or the double itself out of a and b when I call the method like I did under its definition?
Note that both the parameters are used in the same equation
In case classes constructor arguments are vals, so just call:
a.input
b.input
You can also use extractor with the help of unapply method:
val Foo(val1) = a
val Bar(val2) = b
and then use val1 and val2
Update
Then you should use pattern matching on your value:
value match {
case Foo(val1) => val1
case Bar(val1) => val1
}
It works just like val Foo(val1) = a, with using generated unapply method (extractor) in your class, and it is also an expression, so you van assign the result to the variable
If you have multiple arguments just change PatMat construct according to the number of your parameters, in your case:
someMethod(a: Something, b: Something) = (a, b) match {
case (Foo(v1), Foo(v2)) => (v1, v2) // or any other logic with values
case (Foo(v1), Bar(v2)) => // logic for this case
... // logic for other cases
}
The more parameters the more cases you should provide, but you case blank cases if you don't need them
someMethod(a: Something, b: Something) = (a, b) match {
case (Foo(v1), Foo(v2)) => (v1, v2) // or any other logic with values
case _ =>
}
in this case all other cases will be ignored, not the best choice, cause the result type will be incorrect. And you also can black values
someMethod(a: Something, b: Something) = (a, b) match {
case (Foo(v1), _) => v1 // in such case you can work only with v1
... // logic for other cases
}
An alternative to pattern matching could be do redefine your classes like this:
trait Something[T]{
def input:T
}
case class Foo(input: Int) extends Something[Int]
case class Bar(input: Double) extends Something[Double]
Then, any instance of Something will expose the input property. The only potential downside is that it will be of a generic type when you access it.
The alternative approach
In addition to the direct solution of pattern matching in your method, I'll try to show a somewhat more convoluted, general and functional approach to this kind of situations. Still pattern matching is the most direct and simple answer!
If you can explicitly "certify" in your interface the input accessor, you can generalize how you work with the Something class.
In code this translates to
trait Something[T] {
def input: T
}
case class Foo(input: Int) extends Something[Int]
case class Bar(input: Double) extends Something[Double]
from here you can define how to "lift" any function you like to one that works over Somethings
Let's say you have methods that takes two inputs (e.g. Ints or Doubles) and you want to operate on such inputs within one of your case classes (i.e. Foo, Bar)
//this function lift your specific input method to one that takes Somethings
def liftSomething2[T, R](f: (T, T) => R): (Something[T], Something[T]) => R =
(a, b) => f(a.input, b.input)
Let's examine this a bit: it takes a function
(T, T) => R of 2 arguments of type T and a result R
and transforms it in a
(Something[T], Something[T]) => R which takes Somethings as arguments.
Examples
//lifts a function that sums ints
scala> val sumInts = liftSomething2[Int, Int](_ + _)
sumInts: (Something[Int], Something[Int]) => Int = <function2>
//lifts a function that multiplies ints
scala> val multInts = liftSomething2[Int, Int](_ * _)
multInts: (Something[Int], Something[Int]) => Int = <function2>
//lifts a function that divides doubles
scala> val divDbl = liftSomething2[Double, Double](_ / _)
divDbl: (Something[Double], Something[Double]) => Double = <function2>
//Now some test
scala> sumInts(Foo(1), Foo(2))
res2: Int = 3
scala> multInts(Foo(4), Foo(-3))
res3: Int = -12
scala> divDbl(Bar(20.0), Bar(3.0))
res4: Double = 6.666666666666667
//You can even complicate things a bit
scala> val stringApp = liftSomething2[Int, String](_.toString + _)
stringApp: (Something[Int], Something[Int]) => String = <function2>
scala> stringApp(Foo(1), Foo(2))
res5: String = 12
All the above examples lift functions of type (T,T) => R but the "lifting" can be made for all and any argument you need
//This takes three args of different types and returns another type
// the logic doesn't change
def liftSomething3[A,B,C,R](f: (A,B,C) => R): (Something[A], Something[B], Something[C]) => R =
(a,b,c) => f(a.input, b.input, c.input)
//sums to ints and divides by a double
scala> val sumDiv = liftSomething3[Int,Int,Double,Double]((i,j,d) => (i + j) / d)
sumDiv: (Something[Int], Something[Int], Something[Double]) => Double = <function3>
scala> sumDiv(Foo(5), Foo(30), Bar(4.2))
res7: Double = 8.333333333333332
more...
All we've seen so far should be somewhat related to category theory concepts like Applicative Functors and Comonads, but I'm no expert so I encourage you to search for yourself if you feel this sort of abstractions are useful and interesting.
In your example both the a and b have specific types: Foo and Bar respectively. That's why you can simply access their fields like so:
scala> a.input
res4: Int = 10
scala> b.input
res5: Double = 25.1
If however your value has type Something, then you'll need to pattern-match:
val input = somethingOfTypeSomething match {
case Foo(input) => input
case Bar(input) => input
}
The other answers have covered the basic scenario. There are useful variations to consider.
Constructor Pattern
As already answered there is:
value match {
case Foo(x) => x
...
}
Deep Matching
The constructor pattern also supports deep matching. For example, extract x within Bar within Foo which is 3 levels deep:
value match {
case Foo(y, Bar(x)) => x
...
}
Variable Binding
If the value you want to extract is an actual case class inside another case class you can use variable binding. E.g. to extract the whole Bar(x) into b:
value match {
case Foo(y, b # Bar(x)) => b
...
}
Programming in Scala by M. Odersky, Spoon and Venners has a great chapter on case classes and pattern matching which covers many other scenarios. Pattern matching is such a rich part of the language it would be a worthwhile investment.
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.
I started learning scala a few days ago and when learning it, I am comparing it with other functional programming languages like (Haskell, Erlang) which I had some familiarity with. Does Scala has guard sequences available?
I went through pattern matching in Scala, but is there any concept equivalent to guards with otherwise and all?
Yes, it uses the keyword if. From the Case Classes section of A Tour of Scala, near the bottom:
def isIdentityFun(term: Term): Boolean = term match {
case Fun(x, Var(y)) if x == y => true
case _ => false
}
(This isn't mentioned on the Pattern Matching page, maybe because the Tour is such a quick overview.)
In Haskell, otherwise is actually just a variable bound to True. So it doesn't add any power to the concept of pattern matching. You can get it just by repeating your initial pattern without the guard:
// if this is your guarded match
case Fun(x, Var(y)) if x == y => true
// and this is your 'otherwise' match
case Fun(x, Var(y)) if true => false
// you could just write this:
case Fun(x, Var(y)) => false
Yes, there are pattern guards. They're used like this:
def boundedInt(min:Int, max: Int): Int => Int = {
case n if n>max => max
case n if n<min => min
case n => n
}
Note that instead of an otherwise-clause, you simply specifiy the pattern without a guard.
The simple answer is no. It is not exactly what you are looking for (an exact match for Haskell syntax). You would use Scala's "match" statement with a guard, and supply a wild card, like:
num match {
case 0 => "Zero"
case n if n > -1 =>"Positive number"
case _ => "Negative number"
}
I stumbled to this post looking how to apply guards to matches with multiple arguments, it is not really intuitive, so I am adding an random example here.
def func(x: Int, y: Int): String = (x, y) match {
case (_, 0) | (0, _) => "Zero"
case (x, _) if x > -1 => "Positive number"
case (_, y) if y < 0 => "Negative number"
case (_, _) => "Could not classify"
}
println(func(10,-1))
println(func(-10,1))
println(func(-10,0))