what is the difference between case x:Int and case x # Int? In the following example, why doesn't case x#Int gets matched when an Int argument is passed?
scala> def matchInt (x:Any) = x match {
| case x:Int => println("got int"+x) //this matches with Int
| case _ => println("no int "+x)
| }
matchInt: (x: Any)Unit
scala> matchInt(1)
got int1
scala> matchInt("2")
no int 2
scala> def matchInt (x:Any) = x match {
| case x # Int => println("got int"+x) //this doesn't matches with Int
| case _ => println("no int "+x)
| }
matchInt: (x: Any)Unit
scala> matchInt("2")
no int 2
scala> matchInt(1)
no int 1
scala>
x:Int means "x of type Int". x#Int means "x that is a type Int".
The latter is pretty useless in this case.
We use x: Int for Type pattern matching and at times you may want to add a variable to a pattern. You can do this with the following general syntax: variableName # pattern.
For x: Int your pattern matching is fine to match the type of the x.
For variableName # pattern, look the example where we are matching a various pattern:
scala> case class Test(t1: String, t2: String)
defined class Test
scala> object Test2 extends App {
| def matchType(x: Any): String = x match {
| case y # List(1, _*) => s"$y" // works; prints the list
| case y # Some(_) => s"$y" // works, returns "Some(Hiii)"
| case y # Test("t1", "t2") => s"$y" // works, returns "Test(t1,t2)"
| }
| }
defined object Test2
scala> Test2.matchType(List(1,2,3))
res2: String = List(1, 2, 3)
scala> Test2.matchType(Some("Hiii"))
res3: String = Some(Hiii)
scala> Test2.matchType(Test("t1","t2"))
res4: String = Test(t1,t2)
Related
I would like to limit parameter function to have parameter only case class with limited field types. Let's say i would allow only Int and String
def apply[T <: ???](t: T) {
...
}
case class OneParam(int: Int)
case class TwoParams(int: Int, str: String)
case class CompilationErrorClass(list: Array[String])
val oneParam = OneParam(1)
val twoParams = TwoParams(1, "2")
val compilationErrorClass = CompilationErrorClass(List())
val result1 = apply[OneParam](oneParam)
val result2 = apply[TwoParams](twoParams)
val result3 = apply[CompilationErrorClass](compilationErrorClass) // -- this will not compile as has not allowed upper-bound parameter type
How this trick can be done in scala?
Here we go (scala3):
import scala.deriving.Mirror.ProductOf
type AllowTypes = Int *: EmptyTuple | String *: EmptyTuple | (Int, String) | (String, Int)
def size[P <: Product](t: P)
(using p: ProductOf[P],
ev: p.MirroredElemTypes <:< AllowTypes): Int =
Tuple.fromProductTyped(t).size
scala> size(OneParam(0))
val res0: Int = 1
scala> size(TwoParams(1, ""))
val res1: Int = 2
scala> size(CompilationErrorClass(Array()))
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------
1 |size(CompilationErrorClass(Array()))
| ^
| Cannot prove that p.MirroredElemTypes <:< AllowTypes.
1 error found
Or even general solution, allow all case classes with arbitrarily int or string arguments:
scala> type Allowed[T <: Tuple] = T match
| case EmptyTuple => DummyImplicit
| case (Int | String) *: t => Allowed[t]
|
scala> import scala.deriving.Mirror.ProductOf
|
| def size[P <: Product](t: P)
| (using p: ProductOf[P],
| ev: Allowed[p.MirroredElemTypes]): Int =
| Tuple.fromProductTyped(t).size
|
def size[P <: Product](t: P)(using p: deriving.Mirror.ProductOf[P], ev: Allowed[p.MirroredElemTypes]): Int
scala> case class A(i: Int, j: Int, x: String, y: String)
// defined case class A
scala> case class X(x1: Int, x2: String, x3: Int, x4: String, x5: String)
// defined case class X
scala> case class Y(l: Long)
// defined case class Y
scala> size(A(0, 1, "", ""))
val res0: Int = 4
scala> size(X(0, "", 1, "", ""))
val res1: Int = 5
scala> size(Y(0))
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------
1 |size(Y(0))
| ^
| Match type reduction failed since selector Long *: EmptyTuple.type
| matches none of the cases
|
| case EmptyTuple => DummyImplicit
| case (Int | String) *: t => Allowed[t]
1 error found
I start learning Scala and I don't quite understand some behaviors of pattern-matching. Can anyone explain to me why the first case works but the second case doesn't work?
1
def getFirstElement(list: List[Int]) : Int = list match {
case h::tail => h
case _ => -1
}
Scala> getFirstElement(List(1,2,3,4))
res: Int = 1
Scala> 1 :: List(1,2)
res: List[Int] = List(1, 1, 2)
2
def getSumofFirstTwoElement(list: List[Int]): Int = list match {
case List(a: Int, b: Int)++tail => a + b
case _ => -1
}
<console>:11: error: not found: value ++
Scala> List(1,2) ++ List(3,4,5)
res: List[Int] = List(1, 2, 3, 4, 5)
The reason is that there is no unapply method on an object of type ++. The reason that :: works is because behind the scenes it is really:
final case class ::[B](override val head: B, private[scala] var tl: List[B]) extends List[B] {
override def tail : List[B] = tl
override def isEmpty: Boolean = false
}
Source
This leads to how pattern matching is implemented in Scala. It uses extractors (or here), which are basically objects that contain an unapply method, which is provided by default with case classes.
++ is a method on object of list. I think you want:
def getSumofFirstTwoElement(list: List[Int]): Int = list match {
case a::b::tail => a + b
case _ => -1
}
In case two, ++ isn't used for unapply. You want the extractor :: to decompose the list.
A good explanation here.
scala> def getSumofFirstTwoElement(list: List[Int]): Int = list match {
| case a::b::t => a + b
| case _ => -1
| }
getSumofFirstTwoElement: (list: List[Int])Int
scala> getSumofFirstTwoElement(List(1,2,3,4))
res0: Int = 3
Given a regex and a string
val reg = "(a)(b)"
val str = "ab"
and a corresponding case class
case class Foo(a: string, b: string)
How can I match the regex against the string and unapply the matches
into the case class so I have
Foo("a", "b")
in the end?
Pattern match on the result of finding the regular expression in the string and assigning the results to Foo. The API docs for Regex have a similar example.
scala> val reg = "(a)(b)".r
reg: scala.util.matching.Regex = (a)(b)
scala> val str = "ab"
str: String = ab
scala> case class Foo(a: String, b: String)
defined class Foo
scala> val foo = reg.findFirstIn(str) match{
| case Some(reg(a,b)) => new Foo(a,b)
| case None => Foo("","")
| }
foo: Foo = Foo(a,b)
scala> foo.a
res2: String = a
scala> foo.b
res3: String = b
If you are sure that the match is ok, you can currify the function and use a foldleft to apply this version to the list of extracted arguments:
> val reg = "(a)(b)".r
> val str = "ab"
> val f1 = (Foo.apply _).curried
// f1 : String => String => Foo
> val groups = reg.findFirstMatchIn(str).get.subgroups
// groups: List[String]
> val foo = ((f1 : Any) /: groups) { case (f: (String => Any), s) => f(s)} asInstanceOf[Foo]
foo: Foo = Foo(a,b)
See Function2 in scala for the definition of curried.
My code is as follows
val hash = new HashMap[String, List[Any]]
hash.put("test", List(1, true, 3))
val result = hash.get("test")
result match {
case List(Int, Boolean, Int) => println("found")
case _ => println("not found")
}
I would expect "found" to be printed but "not found" is printed. I'm trying to match on any List that has three elements of type Int, Boolean, Int
You are checking for a list containing the companion objects Int and Boolean. These are not the same as the classes Int and Boolean.
Use a Typed Pattern instead.
val result: Option[List[Any]] = ...
result match {
case Some(List(_: Int, _: Boolean, _: Int)) => println("found")
case _ => println("not found")
}
Scala Reference, Section 8.1 describes the different patterns you can use.
The first problem is that the get method returns an Option:
scala> val result = hash.get("test")
result: Option[List[Any]] = Some(List(1, true, 3))
So you'd need to match against Some(List(...)), not List(...).
Next, you are checking if the list contains the objects Int, Boolean and Int again, not if it contains objects whose types are Int, Boolean and Int again.
Int and Boolean are both types and object companions. Consider:
scala> val x: Int = 5
x: Int = 5
scala> val x = Int
x: Int.type = object scala.Int
scala> val x: Int = Int
<console>:13: error: type mismatch;
found : Int.type (with underlying type object Int)
required: Int
val x: Int = Int
^
So the correct match statement would be:
case Some(List(_: Int, _: Boolean, _: Int)) => println("found")
The following also works for Scala 2.8
List(1, true, 3) match {
case List(a:Int, b:Boolean, c:Int) => println("found")
case _ => println("not found")
}
Given the following type and instance:
type operation = (Int, Int) => Int
def add: operation = _ + _
If I try to match an operation in a case statement, Scala complains about unchecked typing due to type erasure:
for (a <- elements) a match {
case o: operation => // do stuff
}
Is there a way to achieve this kind of function-based typing while being erasure-friendly in case statements?
Note, this is similar to this thread.
One easy way to deal with type erasure is to create an unparamaterized class. It's not perfect, but it works. Make it a case class that extends Function2 and it's not even too clunky to use either directly or in a pattern match
scala> case class Operation(f : (Int,Int) => Int) extends ((Int,Int) => Int) {
| def apply(x : Int, y : Int) = f(x,y)
| }
defined class Operation
scala> def add = Operation(_ + _)
add: Operation
scala> val y = add(7,3)
y: Int = 10
scala> val elements = List(1, add, 2)
elements: List[Any] = List(1, <function2>, 2)
scala> for (a <- elements) yield a match {
| case Operation(f) => f(1,2)
| case x : Int => x
| }
res0: List[Int] = List(1, 3, 2)
The limitation is that you have to have "boxed" the operation before you lose its type, not after. Also, you end up with one class per concrete function type.
Another, arguably much better, solution is to not lose the type information. Use an Either to retain the static type info.
scala> val elements : List[Either[Int, (Int, Int) => Int]] = List(Left(1), Right(_ + _), Left(2))
elements: List[Either[Int,(Int, Int) => Int]] = List(Left(1), Right(<function2>), Left(2))
scala> for (a <- elements) yield a match {
| case Right(f) => f(1,2)
| case Left(x) => x
| }
res1: List[Int] = List(1, 3, 2)
The limitation here is that it gets clunky if your List can have more than 2 types. But it effectively avoids forcing Scala to be a dynamically typed language, unlike the previous solution.
If you can wrap a into an Option, then this will work:
scala> val a:Option[Any] = Some(add)
a: Option[Any] = Some(<function2>)
scala> a match { case o:Some[operation] => println ("found"); case _ => }
found