When I send a variable through several pattern matches, what is the naming convention for doing so?
val somethingBetweenOriginalAndResult = original match {
case ...
case ...
}
val somethingElseBetweenOriginalAndResult = somethingBetweenOriginalAndResult match {
case ...
case ...
}
val result = somethingElseBetweenOriginalAndResult match {
case ...
case ...
}
In fact I only need original and result every val introduced in between smutches my namespace and requires me to excogitate a new variable name. How can I avoid this?
Use a block and short temporary variable names (just long enough to suggest to someone what you're trying to accomplish--if it's obvious, x and company will do fine):
val original = ...
val result = {
val x = original match { ... }
val y = x match { ... }
y match { ... }
}
You can even omit variable names entirely if you feel like it (though precedence is backwards so you need to add parens or something):
val result = {
((original match {
...
}) match {
...
}) match {
...
}
}
since the result of the previous match is the argument for the next one.
If you really don't care about somethingElseBetweenOriginalAndResult maybe you should try to avoid creating it at all. It really depends on the type of original and the cases you consider in your pattern matchings, but there is probably a way to achieve that with one pattern matching. Worst case scenario, why not use that :
val result = original match {
case ...
case ...
} match {
case ...
case ...
} match {
case ...
case ...
}
I don't like it really but at least you don't create an unnecessary value which you might accidentally refer to later on.
Related
I am trying to debug a Scala program (this is a build.sbt, but the question is not particular for sbt), where I need to give a partial function for a certain sbt setting. The value for the partial function looks like this
{
case Regex1(a,b,c) =>
case Regex2(d,e,f) =>
...
}
The partial function does not do what I want, so I wanted to debug it. Because I don't know exactly what is passed in, I want to capture the value that is passed into the partial function, but I don't know how to do that.
I could add a case a => println(a) at the beginning of the partial function, but this breaks the whole function.
You can do this:
val print: PartialFunction[InputType, InputType] = { case i => println(i); i }
print andThen {
case Regex1(a,b,c) => ...
case ...
}
I finally figured out how to do it. It is not very elegant, so if anyone knows of a better way, please add another answer!
The solution is to create the partial function explicitly as value:
val result = new PartialFunction[InputType,ResultType] {
def apply(value: InputType) = {
println("input is: " + value) // Yay, I captured the value
value match {
// Same as above
}
}
def isDefinedAt(value: InputType) = true
}
result
Another option would be to match all, and add another match that does the actual work:
{
case value => {
println(value)
value match {
// the original partial function
...
// you might need to add a catch-all that
// does nothing or returns a default value
case _ => None
}
}
}
I have some code for validating ip addresses that looks like the following:
sealed abstract class Result
case object Valid extends Result
case class Malformatted(val invalid: Iterable[IpConfig]) extends Result
case class Duplicates(val dups: Iterable[Inet4Address]) extends Result
case class Unavailable(val taken: Iterable[Inet4Address]) extends Result
def result(ipConfigs: Iterable[IpConfig]): Result = {
val invalidIpConfigs: Iterable[IpConfig] =
ipConfigs.filterNot(ipConfig => {
(isValidIpv4(ipConfig.address)
&& isValidIpv4(ipConfig.gateway))
})
if (!invalidIpConfigs.isEmpty) {
Malformatted(invalidIpConfigs)
} else {
val ipv4it: Iterable[Inet4Address] = ipConfigs.map { ipConfig =>
InetAddress.getByName(ipConfig.address).asInstanceOf[Inet4Address]
}
val dups = ipv4it.groupBy(identity).filter(_._2.size != 1).keys
if (!dups.isEmpty) {
Duplicates(dups)
} else {
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
val taken: Iterable[Inet4Address] = ipAvailability.filter(!_._2).keys
if (!taken.isEmpty) {
Unavailable(taken)
} else {
Valid
}
}
}
}
I don't like the nested ifs because it makes the code less readable. Is there a nice way to linearize this code? In java, I might use return statements, but this is discouraged in scala.
I personally advocate using a match everywhere you can, as it in my opinion usually makes code very readable
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil =>
val ipv4it = ipConfigs.map { ipc =>
InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address]
}
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil =>
val taken = ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys
if (taken.nonEmpty) Unavailable(taken) else Valid
case dups => Duplicates(dups)
}
case invalid => Malformatted(invalid)
}
Note that I've chosen to match on the else part first, since you generally go from specific to generic in matches, since Nil is a subclass of Iterable I put that as the first case, eliminating the need for an i if i.nonEmpty in the other case, since it would be a given if it didn't match Nil
Also a thing to note here, all your vals don't need the type explicitly defined, it significantly declutters the code if you write something like
val ipAvailability: Map[Inet4Address, Boolean] =
ipv4it.map(ip => (ip, isIpAvailable(ip)))
as simply
val ipAvailability = ipv4it.map(ip => (ip, isIpAvailable(ip)))
I've also taken the liberty of removing many one-off variables I didn't find remotely necessary, as all they did was add more lines to the code
A thing to note here about using match over nested ifs, is that is that it's easier to add a new case than it is to add a new else if 99% of the time, thereby making it more modular, and modularity is always a good thing.
Alternatively, as suggested by Nathaniel Ford, you can break it up into several smaller methods, in which case the above code would look like so:
def result(ipConfigs: Iterable[IpConfig]): Result =
ipConfigs.filterNot(ipc => isValidIpv4(ipc.address) && isValidIpv4(ipc.gateway)) match {
case Nil => wellFormatted(ipConfigs)
case i => Malformatted(i)
}
def wellFormatted(ipConfigs: Iterable[IpConfig]): Result = {
val ipv4it = ipConfigs.map(ipc => InetAddress.getByName(ipc.address).asInstanceOf[Inet4Address])
ipv4it.groupBy(identity).filter(_._2.size != 1).keys match {
case Nil => noDuplicates(ipv4it)
case dups => Duplicates(dups)
}
}
def noDuplicates(ipv4it: Iterable[IpConfig]): Result =
ipv4it.map(ip => (ip, isIpAvailable(ip))).filter(!_._2).keys match {
case Nil => Valid
case taken => Unavailable(taken)
}
This has the benefit of splitting it up into smaller more manageable chunks, while keeping to the FP ideal of having functions that only do one thing, but do that one thing well, rather than having god-methods that do everything.
Which style you prefer, of course is up to you.
This has some time now but I will add my 2 cents. The proper way to handle this is with Either. You can create a method like:
def checkErrors[T](errorList: Iterable[T], onError: Result) : Either[Result, Unit] = if(errorList.isEmpty) Right() else Left(onError)
so you can use for comprehension syntax
val invalidIpConfigs = getFormatErrors(ipConfigs)
val result = for {
_ <- checkErrors(invalidIpConfigs, Malformatted(invalidIpConfigs))
dups = getDuplicates(ipConfigs)
_ <- checkErrors(dups, Duplicates(dups))
taken = getAvailability(ipConfigs)
_ <- checkErrors(taken, Unavailable(taken))
} yield Valid
If you don't want to return an Either use
result.fold(l => l, r => r)
In case of the check methods uses Futures (could be the case for getAvailability, for example), you can use cats library to be able of use it in a clean way: https://typelevel.org/cats/datatypes/eithert.html
I think it's pretty readable and I wouldn't try to improve it from there, except that !isEmpty equals to nonEmpty.
I want to replace the below match with an if statement, preferably less verbose than this. I personally find if's to be easier to parse in the mind.
val obj = referencedCollection match{
case None => $set(nextColumn -> MongoDBObject("name" -> name))
case Some( collection) =>....}
Is there an equivalent if statement or some other method that produces the equivalent result?
You can replace the pattern match by a combination of map and getOrElse:
ox match {
case None => a
case Some(x) => f(x)
}
can be replaced by
ox.map(f).getOrElse(a)
You probably ought not indulge yourself in continuing to use if in conditions like this. It's not idiomatic, rarely is faster, is more verbose, requires intermediate variables so is more error-prone, etc..
Oh, and it's a bad idea to use anything with $ signs!
Here are some other patterns that you might use in addition to match:
val obj = referenceCollection.fold( $set(nextColumn -> MongoDBObject("name" -> name) ){
collection => ...
}
val obj = (for (collection <- referenceCollection) yield ...).getOrElse{
$set(nextColumn -> MongoDBObject("name" -> name)
}
val obj = referenceCollection.map{ collection => ... }.getOrElse{
$set(nextColumn -> MongoDBObject("name" -> name)
}
You can basically think of map as the if (x.isDefined) x.get... branch of the if and the getOrElse branch as the else $set... branch. It's not exactly the same, of course, as leaving off the else gives you an expression that doesn't return a value, while leaving off the getOrElse leaves you with an unpacked Option. But the thought-flow is very similar.
Anyway, fold is the most compact. Note that both of these have a bit of runtime overhead above a match or if statement.
There is huge number of options (pun intended):
if (referencedCollection != None) { ... } else { ... }
if (referencedCollection.isDefined) { ... } else { ... } // #Kigyo variant
if (referencedCollection.isEmpty) { /* None processing */ } else { ... }
You could do it like this:
val obj = if(referencedCollection.isDefined) { (work with referencedCollection.get) } else ...
Consider the following structure (in reality the structure is a bit more complex):
case class A(id:String,name:String) {
override def equals(obj: Any):Boolean = {
if (obj == null || !obj.isInstanceOf[A]) return false
val a = obj.asInstanceOf[A]
name == a.name
}
override def hashCode() = {
31 + name.hashCode
}
}
val a1 = A("1","a")
val a2 = A("2","a")
val a3 = A("3","b")
val list = List((a1,a2),(a1,a3),(a2,a3))
Now let's say I want to group all tuples with equal A's. I could implement it like this
list.groupBy {
case (x,y) => (x,y)
}
But, I don't like to use pattern matching here, because it's not adding anything here. I want something simple, like this:
list.groupBy(_)
Unfortunately, this doesn't compile. Not even when I do:
list.groupBy[(A,A)](_)
Any suggestions how to simplify my code?
list.groupBy { case (x,y) => (x,y) }
Here you are deconstructing the tuple into its two constituent parts, just to immediately reassemble them exactly like they were before. In other words: you aren't actually doing anything useful. The input and output are identical. This is just the same as
list.groupBy { t => t }
which is of course just the identity function, which Scala helpfully provides for us:
list groupBy identity
If you want to group the elements of a list accoding to their own equals method, you only need to pass the identity function to groupBy:
list.groupBy(x=>x)
It's not enough to write list.groupBy(_) because of the scope of _, that is it would be desugared to x => list.groupBy(x), which is of course not what you want.
My code is becoming littered with the following code pattern:
val opt = somethingReturningAnOpt
if (opt.isDefinedAt) {
val actualThingIWant = opt.get
}
Is there some way to simplify this? (it seems needlessly complex and a code smell). Ideally it would be something like:
if (Some(actualThingIWant) = somethingReturningAnOpt) {
doSomethingWith(actualThingIWant)
}
Is anything like that possible?
Maybe something like this:
somethingReturningAnOpt match {
case Some(actualThingIWant) => doSomethingWith(actualThingIWant)
case None =>
}
or as pst suggests:
somethingReturningAnOpt.foreach { actualThingIWant =>
doSomethingWith(actualThingIWant)
}
// or...
for (actualThingIWant <- somethingReturningAnOpt) {
doSomethingWith(actualThingIWant)
}
The canonical guide to Option wrangling is by Tony Morris.
Or:
somethingReturningAnOpt.map(doSomethingWith(_))
As in in:
val str = Some("foo")
str.map(_.toUpperCase)
... and use flatMap when the result of doSomethingWith is an Option itself.
val index = Option(Map("foo" -> "bar"))
index.flatMap(_.get("whatever")) // Returns None :-)
index.map(_.get("whatever")) // Returns Some(None) :-(
The following code cannot do something useful, since after the if, actualThingIWant is not always defined and as such this code will not compile, as long as you try to use actualThingIWant later.
val opt = somethingReturningAnOpt
if (opt.isDefinedAt) {
val actualThingIWant = opt.get
}
So, you have to provide a default value. This can be achieved with getOrElse:
val thingIWant = opt.getOrElse(myDefaultValue)
Or if you don't want to have actualThingIWant after the body of the if, which means you want to trigger some side-effects only if the option is defined, you can write:
opt.foreach{ thingIWant =>
println(thingIWant)
}
or a bit shorter
opt.foreach(println)