I have an Option, say O, which can either be None or may have some value inside. If it has some value, that value may have a flag, say f. My requirement is that if O is None, then I create an object, say of type MyEntity,but if O has a value with flag as true, I return Nil else I create instance of MyEntity with different value. Java code can be almost as:
if(O.isEmpty) {
MyEntity("x")
} else {
if(O.f) {
Nil
} else {
MyEntity("y") // with different value
}
}
I want to do this in functional style using HoFs provided in scala.Option. What would be best possible way of doing it? I could this so far :
if(O.isEmpty){
MyEntity("x")
} else {
Option.unless(O.exists(_.f))(MyEntity("y"))
}
I misread your question the first go round, so here is attempt #2
This is a great case for pattern matching:
val maybeMyEntity: Option[MyEntity] = option match {
case Some(flagValue) if flagValue => None
// case Some(true) => None (More concise, but does not highlight a handy feature)
case Some(_) => Some(MyEntity("Y"))
case None => Some(MyEntity("X"))
}
Pattern matching is very powerful.
Alternatively, mapping is another option:
mapping of an Option will only occur if its value is not empty, and flatMapping will remove the layer of Option added, from Option[Option[MyEntity]] to Option[MyEntity]
val result: Option[MyEntity] = if (option.isEmpty) {
Some(Entity("X"))
} else {
option.flatMap { flagValue =>
if (flagValue) {
None
} else {
Some(MyEntity("Y"))
}
}
}
As mentioned in the comments, Nil type is a List, and your expression should always return the same type (should really not be Any).
One possibility is to define a "sentinel" value for MyEntity, e.g.:
object MyEntity {
val Nil = MyEntity("")
}
Now you can write:
val result = O.fold(MyEntity("x")) {
case true => MyEntity.Nil
case false => MyEntity("y")
}
case class AnOption(var flag: Boolean = true)
case class MyEntities(name: String)
val myOptionEntityCheck = (someOption: Option[AnOption]) => {
someOption match {
case None => Some(MyEntities("X"))
case Some(aValue: AnOption) if aValue.flag => None
// OR case Some(AnOption(true)) => None
case Some(_) => Some(MyEntities("y"))
}
}
Related
I am moving from C / C++to Scala, and following is my code -
something match {
case one: {
if (some_list.nonEmpty) {
if (some_list size == 1 && some_list contains == something)
fill a form(use something in a certain way)
else if (some_list size == 1 && some_list contains == soemthing_else)
fill a form(use something_else in a certain way)
else {
if (some_var.nonEmpty) {
fill a form(use some_var)
} else {
fill a form(without using some_var)
}
}
} else {
if (another_var has certain value || another_var has certain value 2) {
fill a form(using another_var)
} else {
fill a form(without using another_var)
}
} //if (some_list.nonEmpty) ends
} // case ends
case two: one liner code
case _: one liner code
} //match ends
Looking
for guidance to write it in a nice scala way using its features and strengths.
Thank you
I am making a few assumptions to make this work:
trait cases
case object one extends cases
case object two extends cases
case object three extends cases
case object four extends cases
val someList: List[cases] = List(one, two)
val something: cases = one
val somethingElse: cases = two
val someVar: Option[String] = Option("someVar")
val someOtherVar: Option[String] = Option("someOtherVar")
val anotherVar: Option[String] = Option("anotherVar")
Following is a simplified version of your code using the above:
something match {
case `one` =>
someList match {
case head :: Nil if(head == something) => println("one")
case head :: Nil if(head == somethingElse) => println("two")
case head :: tail if(someVar.nonEmpty) => println("someVar")
case head :: tail if(someOtherVar.nonEmpty) => println("someOtherVar")
case head :: tail => println("not using someOtherVar")
case Nil if(anotherVar.nonEmpty) => println("anotherVar")
case Nil => println("not using anotherVar")
}
case `two` => println("two")
case _ => println("rest")
}
Given a list of inputs that could be valid or invalid, is there a nice way to transform the list but to fail given one or more invalid inputs and, if necessary, to return information about those invalid inputs? I have something like this, but it feels very inelegant.
def processInput(inputList: List[Input]): Try[List[Output]] = {
inputList map { input =>
if (isValid(input)) Left(Output(input))
else Right(input)
} partition { result =>
result.isLeft
} match {
case (valids, Nil) =>
val outputList = valids map { case Left(output) => output }
Success(outputList)
case (_, invalids) =>
val errList = invalids map { case Right(invalid) => invalid }
Failure(new Throwable(s"The following inputs were invalid: ${errList.mkString(",")}"))
}
}
Is there a better way to do this?
I think you can simplify your current solution quite a bit with standard scala:
def processInput(inputList: List[Input]): Try[List[Output]] =
inputList.partition(isValid) match {
case (valids, Nil) => Success(valids.map(Output))
case (_, invalids) => Failure(new Throwable(s"The following inputs were invalid: ${invalids.mkString(",")}"))
}
Or, you can have a quite elegant solution with scalactic's Or.
import org.scalactic._
def processInputs(inputList: List[Input]): List[Output] Or List[Input] =
inputList.partition(isValid) match {
case (valid, Nil) => Good(valid.map(Output))
case (_, invalid) => Bad(invalid)
}
The result is of type org.scalactic.Or, which you then have to match to Good or Bad. This approach is more useful if you want the list of invalid inputs, you can match it out of Bad.
scalaz's validation is designed exactly for this. Try reading the tale of three nightclubs for how this would work, but the body of your function would probably end up just consisting of something like:
def processInput(inputList: List[Input]): Validation[List[Output]] = {
inputList foldMap { input =>
if (isValid(input)) Failure(Output(input))
else Success(List(input))
}
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.
Say I have defined:
sealed trait ValidationResult
object Valid extends ValidationResult
case class Invalid(error:String) extends ValidationResult
Then I have a list which contains some validation results:
val results = List(Valid, Invalid("required username"), Valid)
I want to find the first one which has the type of Invalid:
results.find( ??? ) match {
case Some(error) => println("Invalid: " + error)
case _ => println("No error")
}
You can see there is a ??? in the find, but I don't know what shall I input there.
results.find {
case Invalid(_) => true
case _ => false
} match {
case Some(Invalid(error)) => println("Invalid: " + error)
case _ => println("No error")
}
or maybe:
results.collectFirst {
case Invalid(error) => error
} match {
case Some(error) => println("Invalid: " + error)
case _ => println("No error")
}
Not needed as is, but I would convert Valid into a case object, in case you want to pattern-match it.
Note: both versions return the first error found. If you want to accumulate errors, then you need to filter and map (or fold).
Using list span,
val (v,inv) = results.span { _ match {
case Invalid(_) => false
case _ => true
}
}
which returns
(v, inv) = (List(Valid),
List(Invalid(required username), Valid))
In the resulting pair,
the first element is a list of all valid results occurring from the beginning of the list,
the second element is a list that starts with the first item that did not hold the predicate.
Thus we can extract the first invalid item with
if (inv.isEmpty)
Valid
else
inv.head
res: Invalid(required username)
I use a Java method that returns an object or null if a value was not found. So I need to check for null values:
val value = javaobject.findThing(xyz)
if(value != null) {
value.doAnotherThing()
} else {
warn("Value not found.")
}
Can I write this code shorter with the Box concept? I have read the Lift-Wiki-documentation about the Box concept, but I don't understand how to use it with Java null values.
#TimN is right, you could use Box(value) to create a Box from a possibly null value, but you'll get a deprecation warning.
scala> val v: Thing = null
v: Thing = null
scala> Box[Thing](v)
<console>:25: warning: method apply in trait BoxTrait is deprecated: Use legacyNullTest
Box[Thing](v)
While you could use Box.legacyNullTest, if this is what you're doing, then I would just stick with the standard library and use Option.
Option(javaobject.findThing(xyz)) match {
case Some(thing) => thing.doAnotherThing()
case _ => warn("Value not found.")
}
And if you needed a Box to pass around, Option will automagically convert to a Box:
scala> val b: Box[Thing] = Option(v)
b: net.liftweb.common.Box[Thing] = Empty
Similar to Scala's Option, you can simply call Box() and pass in the value that may or may not be null, and you'll get a Box object that can be used normally. For example:
Box(javaobject.findThing(xyz)) match {
case Full(thing) => thing.doAnotherThing()
case _ => warn("Value not found.")
}
You can use Box.legacyNullTest. This method encapsulates any object in a Box in a null-safe manner:
def legacyNullTest[T](in: T): Box[T] = in match {
case null => Empty
case _ => Full(in)
}
Box returned from legacyNullTest can be later used as usual in for comprehensions or in pattern matching:
for {
fragment <- Box.legacyNullTest(uri.getFragment)
yield {
doSth(fragment)
}
or
Box.legacyNullTest(uri.getFragment) match {
case Full(fragment) =>
doSth(fragment)
case _ =>
log.error("Missing fragment part")
doSthElse
}