I have an Option of a string. I want to update the contained value:
if(x.isEmpty) {
...another calculation
} else {
x.map(val => ...update val)
}
Is this an idiomatic way?
x.fold(another calculation)(v => ...update v)
e.g.
x.fold("no value")("Value is " + _)
Note that this extracts the value from the Option so if you want to have the result as an Option you need to wrap it in Some.
Note that if your inner computation gets too long or unreadable for a fold, there's always good old-fashioned pattern matching.
x match {
case None => {
// None case ...
}
case Some(y) => {
// Some case (y is the inside) ...
}
}
Like everything in Scala, this is an expression, so it can be assigned to a variable or used in another other expression you like.
Alternatively, using the lazy keyword:
// T is the type of the value
val valueOpt: Option[T] = ???
lazy val backupValue: T = ??? // Other calculation, but only evaluated if needed
val value: T = valueOpt.map( v => /*Update v*/ ).getOrElse( backupValue )
// do something with the value you want to manipulate
Related
I'm using pattern matching in scala a lot. Many times I need to do some calculations in guard part and sometimes they are pretty expensive. Is there any way to bind calculated values to separate value?
//i wan't to use result of prettyExpensiveFunc in body safely
people.collect {
case ...
case Some(Right((x, y))) if prettyExpensiveFunc(x, y) > 0 => prettyExpensiveFunc(x)
}
//ideally something like that could be helpful, but it doesn't compile:
people.collect {
case ...
case Some(Right((x, y))) if {val z = prettyExpensiveFunc(x, y); y > 0} => z
}
//this sollution works but it isn't safe for some `Seq` types and is risky when more cases are used.
var cache:Int = 0
people.collect {
case ...
case Some(Right((x, y))) if {cache = prettyExpensiveFunc(x, y); cache > 0} => cache
}
Is there any better solution?
ps: Example is simplified and I don't expect anwers that shows that I don't need pattern matching here.
You can use cats.Eval to make expensive calculations lazy and memoizable, create Evals using .map and extract .value (calculated at most once - if needed) in .collect
values.map { value =>
val expensiveCheck1 = Eval.later { prettyExpensiveFunc(value) }
val expensiveCheck2 = Eval.later { anotherExpensiveFunc(value) }
(value, expensiveCheck1, expensiveCheck2)
}.collect {
case (value, lazyResult1, _) if lazyResult1.value > 0 => ...
case (value, _, lazyResult2) if lazyResult2.value > 0 => ...
case (value, lazyResult1, lazyResult2) if lazyResult1.value > lazyResult2.value => ...
...
}
I don't see a way of doing what you want without creating some implementation of lazy evaluation, and if you have to use one, you might as well use existing one instead of rolling one yourself.
EDIT. Just in case you haven't noticed - you aren't losing the ability to pattern match by using tuple here:
values.map {
// originial value -> lazily evaluated memoized expensive calculation
case a # Some(Right((x, y)) => a -> Some(Eval.later(prettyExpensiveFunc(x, y)))
case a => a -> None
}.collect {
// match type and calculation
...
case (Some(Right((x, y))), Some(lazyResult)) if lazyResult.value > 0 => ...
...
}
Why not run the function first for every element and then work with a tuple?
Seq(1,2,3,4,5).map(e => (e, prettyExpensiveFunc(e))).collect {
case ...
case (x, y) if y => y
}
I tried own matchers and effect is somehow OK, but not perfect. My matcher is untyped, and it is bit ugly to make it fully typed.
class Matcher[T,E](f:PartialFunction[T, E]) {
def unapply(z: T): Option[E] = if (f.isDefinedAt(z)) Some(f(z)) else None
}
def newMatcherAny[E](f:PartialFunction[Any, E]) = new Matcher(f)
def newMatcher[T,E](f:PartialFunction[T, E]) = new Matcher(f)
def prettyExpensiveFunc(x:Int) = {println(s"-- prettyExpensiveFunc($x)"); x%2+x*x}
val x = Seq(
Some(Right(22)),
Some(Right(10)),
Some(Left("Oh now")),
None
)
val PersonAgeRank = newMatcherAny { case Some(Right(x:Int)) => (x, prettyExpensiveFunc(x)) }
x.collect {
case PersonAgeRank(age, rank) if rank > 100 => println("age:"+age + " rank:" + rank)
}
https://scalafiddle.io/sf/hFbcAqH/3
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 two vals, a condition and an option. Note that condition is a simple boolean, not depending on the option's value.
If condition holds true, I would like to map over the option to convert it to a result value. In all other cases, I would like to return a defaultResult.
This works and is quite readable, but I dislike the duplication of defaultResult:
val result = if (condition) {
option.map(valueToResult).getOrElse(defaultResult)
} else {
defaultResult
}
My second approach does not have duplications, but I dislike the fact that filter is abused for something that is not actually dependent on the option's value:
val result = option.filter(_ => condition).map(valueToResult).getOrElse(defaultResult)
What's a more idiomatic or otherwise better approach in Scala?
You can use Option.collect:
Returns a scala.Some containing the result of applying pf to this
scala.Option's contained value, if this option is nonempty and pf is
defined for that value.
val result = option.collect {
case x if condition => valueToResult(x)
}.getOrElse(defaultResult)
val result = option match {
case Some(value) if condition => valueToResult(value)
case _ => defaultResult
}
val result = (for (v<-option if condition) yield valueToResult(v)).getOrElse(defaultResult)
option.foldLeft(defaultResult)((d,x) => if (condition) valueToResult(x) else d)
val result = (condition match {
case true => option.map(valueToResult)
case false => None
}).getOrElse(defaultResult)
I have an piece of code where i have used the pattern matching inside it i have used map in all cases, i want to get the output of what map is giving to a variable. Below is my code:
override def run():List[Option[Student]] =
StudentDataCache.get(surname) match {
case Some(i) => i.otherSiblings.map(siblings =>
StudentDataCache.get(siblings) match {
case Some(i) => Some(i)
case None=> getStudentFromDatabase(siblings)
}
)
case None =>
getStudentFromDatabase(surname).get.otherSiblings.map(siblings => StudentDataCache.get(siblings) match {
case Some(i) => Some(i)
case None=> getStudentFromDatabase(siblings)
}
)
}
output of both the map statement inside case is List[Option[Student]], is there a way to get this into variable because i want to convert this list into a single object since HystrixCommand execute output does not supports List as output. I want to convert it to StudentListing(val listing: List[Option[Student]])
just... assign it to a value / variable:
override def run(): StudentListing = {
val result = StudentDataCache.get(surname) match { /* same code*/ }
StudentListing(result) // or however you wrap it into a StudentListing...
}
A match expression, like any other expression in Scala, is evaluated into a value - you can do whatever you want with this value.
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.