I was wondering what do you guys think is more idiomatic and better in terms of performance.
Over a monad of type Option or Try, use pattern matching or map and getOrElse in case you want to control the side effect.
So what do you think is better this:
maybeConnectTimeout
.map(connectTimeout => session.connect(connectTimeout))
.getOrElse(session.connect())
Or this
maybeConnectTimeout match {
case Some(connectTimeout) => session.connect(connectTimeout)
case None => session.connect()
}
what ... is ... better in terms of performance.
According to odersky pattern match
I am surprised that the pattern match gets so little love here. Not only is it by far the fastest (probably at least 10 times as fast as the alternatives), it is also the clearest.
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.
Disclaimer: I'm much more experienced in Java than in Scala (which I'm learning).
In Java, I have read several times that switch could be harmful to object orientation, especially when used against types (this kind of problems also led to this: http://www.antiifcampaign.com/).
In Scala, one of the introduction video lessons of Martin Odersky shows how pattern matching is a better alternative to multiple "low-level" isInstanceOf checks.
Although, pattern matching catches more flexible patterns than a simple Java switch, I still see the first as a generalization of the latter.
Don't pattern matching and "switch on types" roughly share the same fundamental approach?
Isn't pattern matching just some syntactic sugar hiding lots of isInstanceOf/asInstanceOf?
If not, how would pattern matching be more flexible (as in: resilient to change) than writing those low level checks ourselves (apart from the error-prone nature of this tedious task)?
Pattern matching may well be "syntactic sugar", in some senses, but it is a lot more than a code replacement.
a) The match construct will enforce completeness on the domain by making sure there are no uncovered cases, unless you specifically indicate that it is only a partial function.
b) The match syntax is much less verbose.
c) The match construct includes guards, predicates that qualify the match.
def mergeSort(a: List[Int], b: List[Int]): List[Int] = (a, b) match {
case (ah :: at, bh :: bt) if ah < bh =>
ah :: mergeSort(at, b)
case (ah :: at, bh :: bt) if ah >= bh =>
bh :: mergeSort(a, bt)
case (Nil, b) =>
b
case (a, Nil) =>
a
}
d) Deconstruction
...
case Node(x, Empty, Empty) =>
x - 52
...
The declarative form is much easier to read (given a bit of experience).
Edit: the term "syntactic sugar" is used frequently about Scala. However, type inferencing and "aggressive" type resolution such as is used in pattern matching make for powerful language constructs. As for the original question, pattern matching is independent of object orientation and provides powerful, type safe methods for accessing data that may make the use of classes less of a requirement.
If you have n types of nouns with m types of verbs, you have n x m noun-verb combinations to keep track of.
There are two good strategies for managing this complexity.
You can organize your code by nouns (classes), and each class needs to be able to deal with every verb (method). This makes it easy to add new nouns. Of course, when you add a new verb, it's annoying to go through each of your nouns and make sure that it can handle the new verb. This is the object-oriented approach.
You can also organize your code by verbs (functions), and each function needs to be able to deal with every noun (pattern). This makes it easy to add new verbs, Of course, when you add a new noun, it's annoying to go through each of your verbs and make sure that it can handle the new noun. This is the functional approach.
There's nothing clearly better about either approach, and both have been used very successfully. You can run into problems when you try to mix them, though, so generally you want to be very clear about which approach you are using for a given problem.
In Scala, pattern matching is done via extractors. Extractors are themselves objects and only have access to the public API of the object that is being pattern matched against.
Thus, pattern matching does not break object encapsulation.
Theoretically, all conditionals (including pattern matching) can be replaced by polymorphism. See Smalltalk as an example of a language which has no conditionals, no loops, no switches, no control structures of any kind except polymorphic message dispatch. And see Newspeak as an example of a Smalltalk-inspired language which has powerful pattern matching inspired by Scala and F# implemented as a library completely on top of polymorphic message dispatch.
When done this way, pattern matching is as object-oriented as can be.
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.
The syntax for match expressions is pretty nice:
expr match {
case Test(l1) => ...
...
}
But it's driving me nuts that I don't understand the motivation to why this this syntax is used instead of match (expr) ..., like branching statements in a decent C descendant!
I see no reasonably explanation for this. And I don't find the answer neither in Programming in Scala, the Scala web site, in this paper, this thesis, here on SO nor on the rest of the web.
It's not that it's anything wrong with it, just that it's a complete mystery. And when match work in this way, why not if and for also?
Does anybody know? I don't think I can stand using the language any longer without finding this out. I think about it all the time. I can hardly sleep at night.
To take a similar bit of Scala syntax, guards in pattern matching cases don't require parentheses around their conditional expressions—e.g., the following:
case i if i % 2 == 0 => i / 2
Is just as valid as this:
case i if (i % 2 == 0) => i / 2
Sticking to the C-family style would mean requiring the latter form, even though the parentheses aren't necessary for disambiguation. The Scala language designers decided that reducing line noise trumped maintaining the family resemblance in this case.
I'd guess that a similar motivation is at work in the match syntax, and to my eye match (expr) { ... } does indeed look pretty awful (and misleading) compared to expr match { ... }.
Also, just this afternoon I refactored someone else's x match { ... } on an Option to x map { ... } instead. match as an infix operator makes the similarity between these two expressions clear.
On the issue of why match isn't just a method, here's a five year-old question from David Pollak on the scala-debate mailing list:
Why is 'match' a language level construct rather than a method on Any?
And Martin Odersky's answer:
It used to be that way in Scala 1. I am no longer sure why we changed.
syntax highlighting? error reporting? not sure. I don't think it
matters much either way, though.
I'm with Martin on this one.
Note that there are a couple of practical differences (apart from the simple "dot or not" question). For example, this doesn't compile:
def foo[A, B](f: PartialFunction[A, B])(a: A) = a match f
If match were still a method on Any, requiring a literal bunch of cases would be a rather strange requirement.
Seeking a more elegant solution
I have this piece of code, I just use it in test cases where it isn't necessary to do any error handling. What it does is:
take an input list of strings
parse them using the DSJSonmapper.parseDSResult method
filters them and extracts the Right value from each Either (Left is an Exception)
The code is as follows:
def parseDs(ins: List[String]) = {
def filterResults[U, T](in: List[Either[U, T]]): List[T] = {
in.filter(y => y.isRight).map(z => z.right.get)
}
filterResults(ins.map(x => DSJsonMapper.parseDSResult(x)))
}
Now, I haven't done an awful lot of polymorphic functions, but this works. However I feel like it's a bit ugly. Has anyone got a better suggestion, how to accomplish the same thing.
I'm aware this is going to come down to a case of personal preference. But suggestions are welcome.
collect is made for exactly this kind of situation:
def filterMe[U,T](in: List[Either[U,T]]): List[T] = in.collect{
case Right(r) => r
}
In fact, it's so good at this you may want to skip the def and just
ins.map(DSJsonMapper.parseDsResult).collect{ case Right(r) => r }
Rex's answer is possibly a little clearer, but here's a slightly shorter alternative that parses and "filters" in a single step:
ins.flatMap(DSJsonMapper.parseDSResult(_).right.toOption)
Here we take the right projection of each parse result and turn that into an Option (which will be None if the parse failed and Some(whatever) otherwise). Since we're using flatMap, the Nones don't appear in the result and the values are pulled out of the Somes.