I often find myself wanting to clump multiple matchers / extractors into the one line, but this doesn't seem to be allowed. e.g.:
text match {
case regex1(a) | regex2(a) => a + "-"
}
(even though a is the same type for both matchers)
so I'm forced to refactor like this (which can get ugly when there are several of these, all handling different matches, mixed with inline responses)
text match {
case regex1(a) => op(a)
case regex2(a) => op(a)
}
def op(a: String) = a + "-"
is there a cleaner way? And will this be supported in Scala in the future?
No, this is not possible in the general case. However, there are a few workaround that might be use to combine pattern matching cases:
Match on a super class of the cases you are willing to group
Use the case a # _ if boolexpr(a) or boolexpr(a) => construction
Factorize the common code in a function, like you did in your example
And probably others. I don't think this is going to change any time soon as it would encourage writing cryptic mach/cases.
Related
How can I avoid always putting
case _ =>
at the end in Scala matching? It is sometimes possible that other values will be matched, but I only want to do something with the cases above the
"case _ =>"
case.
A match is a function like most things in Scala, so it returns a value and you need to return something for every possible case. If you are not doing anything in case _ then you are returning Unit which, in turn, means that the code is relying on side effects and is non-functional.
So the best way to reduce the use of empty case _ => in your code is to make it more functional, since this isn't used in functional code.
The alternative is to use a different mechanism for a multi-way branch, such as chained if, or chains of Option/orElse, or find/collectFirst on a list of operations.
If I run the following code, then I get an error:
import scala.reflect.ClassTag
class General {
}
class SubGeneral extends General {
def test() = println("tested")
}
class ProGeneral[T <: General: ClassTag] {
var array = Array.ofDim[T](3, 3)
def funcForSubGeneral(): Unit =
if (array(0)(0).isInstanceOf[SubGeneral]) then array(0)(0).test()
}
That is because General does not have the function test().
I know that I can fix this with pattern matching. This instead of the above funcForSubGeneral() works:
def funcForSubGeneral(): Unit =
array(0)(0) match {
case s: SubGeneral => s.test()
case _ => println("nope")
}
But I was wondering if it is possible to get the runtime type of array(0)(0) and check if it is a SubGeneral, and if that is the case then I call test(), which shouldn't cause a problem?
That is what I was actually trying by using isIntanceOf. I want to omit pattern matching since I'm just interested in one type.
isInstanceOf doesn't change anything, you would need to do array(0)(0).asInstanceOf[SubGeneral].test() in order to force the casting.
Note that the casting may fail at runtime, so that is why you need to check with the if before. Thus the end code looks like this:
if (array(0)(0).isInstanceOf[SubGeneral]) then array(0)(0).asInstanceOf[SubGeneral].test()
But, since this is cumbersome and error-prone, we have pattern matching:
array(0)(0) match {
case subGeneral: SubGeneral => subGeneral.test()
}
However, note that type tests are considered a bad practice; mainly because they are actually class checks and may fail certain circumstances. For example:
List("a", "b", "c") match {
case numbers: List[Int] => numbers.head + 1
}
Will throw an exception in runtime since, due to type erasure, we lost the [String] part and it matches only List then it tries to read the first element as an Int which is an error.
Anyways, this is the fourth time in two days you ask a question that shows bad practices and unidiomatic code.
My advice:
I would bet you are not following an appropriate resource to learn the language. Rather, it seems you are just trying to mimic another language (Python?) with different syntax. - Thus, I encourage you to pick an appropriate book, course, tutorial, etc; that properly introduces the language and its idioms.
I would encourage you to join the official Discord server which is more suitable for newcomers than StackOverflow.
I would recommend you to explain the meta-problem you are trying to solve in detail (either here in a new question or in the Discord server), I am pretty sure there are better and more idiomatic ways to solve it.
I am writing by hand a recursive-descent parser for a small language. In my lexer I have:
trait Token{def position:Int}
trait Keyword extends Token
trait Operator extends Token
case class Identifier(position:Int, txt:String) extends Token
case class If (position:Int) extends Keyword
case class Plus (position:Int) extends Operator
/* etcetera; one case class per token type */
My parser works well, and now I would like to incorporate some error recovery: replacing, inserting or discarding tokens until some synchronization point.
For that, it would be handy to have a function that, in invalid Scala, would look something like this
def scanFor(tokenSet:Set[TokenClass], lookahead:Int) = {
lexer.upcomingTokens.take(lookahead).find{ token =>
tokenSet.exists(tokenClass => token.isInstanceOf[tokenClass])
}
}
which I would call, for example: scanFor(Set(Plus, Minus, Times, DividedBy), 4)
However TokenClass, of course, is not a valid type, and I don't know how to create the previous set.
As alternatives:
I could just create a new trait and make all the token classes in the token set I want to check against extend that trait, and then just do an instanceOf check against that trait. However, I may have several of those sets, which could make them hard to name, and the code hard to maintain later on.
I could create isXXX:Token=>Boolean functions, and make sets of those, but it seems unelegant
Any suggestions?
I actually recommend, if there are only a handful of such combinations, using an additional trait. It's easy to write and understand, and it will be fast at runtime. It's not really so bad to say
case class Plus(position: Int)
extends Operator with Arithmetic with Precedence7 with Unary
But there are a wide range of alternatives.
If you don't mind a finicky manual maintenance process and need something really fast, defining an ID number (which you must manually keep distinct) for each token type will allow you to use Set[Int] or BitSet or even just a Long to select those classes you like. You can then do set operations (union, intersection) to build up these selectors from each other. It's not hard to write unit tests to help make the finicky bit a little more reliable. If you can at least manage to list all your types:
val everyone = Seq(Plus, Times, If /* etc */)
assert(everyone.length == everyone.map(_.id).toSet.size)
So you shouldn't be too alarmed by this approach if you decide the performance and composability are essential.
You can also write custom extractors that can (more slowly) pull out the right subset of tokens by pattern matching. For example,
object ArithOp {
def unapply(t: Token): Option[Operator] = t match {
case o: Operator => o match {
case _: Plus | _: Minus | _: Times | _: DividedBy => Some(o)
case _ => None
}
case _ => None
}
}
will give you None if it's not the right type of operation. (In this case, I'm assuming there's no parent other than Operator.)
Finally, you could probably express your types as unions and HLists and pick them out that way using Shapeless, but I don't have experience personally doing that with a parser, so I'm not sure of what difficulties you might encounter.
I'm a fairly new Scala developer. I am an experienced Java developer and so far I've been enjoying Scala's simplicity. I really like the functional constructs and quite often they force you to write cleaner code. However recently I noticed due to comfort and simplicity I end up using constructs I wouldn't necessarily use in Java and would actually be considered a bad practice e.g.
private def convertStringToSourceIds(value: String) : Seq[Integer] = {
Try(value.split(",").toSeq.map(convertToSourceId(_))).getOrElse(Seq())
}
The same code snippet can be written as
private def convertStringToSourceIds(value: String) : Seq[Integer] = {
if(value!=null) value.split(",").toSeq.map(convertToSourceId(_)) else Seq()
}
A part of me realizes that the Try/getOrElse block is designed with Options in mind but quite often it makes code more readable and handles cases you might have missed (which of course isn't always a good thing).
I would be interested to know what is the opinion of an experienced Scala developer on the matter.
I am not claiming any "experience" title but I much prefer your second construct for a few reasons
Throwing an exception (an NPE in this case) is expensive and best avoided; it should remain just that, exceptional
if are expressions in Scala, which avoids declaring "dangling" variables to hold the result of the test (just like ternary operators). Alternatively the match..case construct provides for very readable code.
I would personally return an Option[Seq[Integer]] to "pass back" the information that the values was null and facilitate further chaining of your function.
Something like
private def convertStringToSourceIds(value: String) : Option[Seq[Integer]] = value match {
case null => None
case _ => Some(value.split(",").map(convertToSourceId(_)))
}
note 1: not sure you need the toSeq
note 2: for good or bad, looks a bit Haskellish
The combination of Scala + FP makes it almost doubly certain you will get different opinions :)
Edit
Please read comments below for additional reasons and alternatives, i.e,
def convertStringToSourceIds(value: String): Option[Array[String]] = Option(value).map(_.split(",").map(convertToSourceId(_)))
If you can, use Options instead of null to show when a value is missing.
Assuming that you can't use Options, a more readable way to handle this could be
private def convertStringToSourceIds(value: String) : Seq[Integer] = value match {
case null => Seq();
case s => s.split(",").toSeq.map(convertToSourceId(_));
}
I've been playing with scala pattern matching recently and was wondering whether there is a way to create an extractor inside of the case statement. The following code works, but you have to define the extractor first and assign it to a val:
val Extr = "(.*)".r
"test" match {
case Extr(str) => println(str)
}
What I would like to do, or what I would like someone to confirm is impossible, is something like this:
"test" match {
case ("(.*)".r)(str) => println(str)
}
EDIT: In case anyone from the scala team is reading this: Would it be feasible to implement this?
Unfortunately it is not possible and I see no way to simplify your first example.
The case statement has to be followed by a Pattern. The Scala Language Specification shows the BNF of patterns in section 8.1. The grammar of patterns is quite powerful, but is really just a pattern, no method calls or constructors are allowed there.
I had a similar problem and i solved it like this:
case x if x.matches("regex") => foo(x)
I don't know if this is exactly what you want, but it works