I want to do something like this:
def or[A](x: Option[A], y: Option[A]) = x match {
case None => y
case _ => x
}
What is the idiomatic way to do this? The best I can come up with was Seq(x, y).flatten.headOption
It's already defined for Option:
def or[A](x: Option[A], y: Option[A]) = x orElse y
in scalaz, you can use the Plus typeclass for this:
scala> 1.some <+> 2.some
res1: Option[Int] = Some(1)
scala> none[Int] <+> 2.some
res2: Option[Int] = Some(2)
scala> none[Int] <+> none[Int]
res3: Option[Int] = None
If, for some reason, you don't want to use orElse, well, there's always another way to do it in Scala.
def or[A](xOpt: Option[A], yOpt: Option[A]) = xOpt.map(Some(_)).getOrElse(yOpt)
Related
Is there a predefined function x in Scala that combine 2 Options so that
Some(a) x None => Some(a)
None x Some(b) => Some(b)
None x None => None
Yes, this is the orElse method. It chooses the first defined value, or None if neither is defined.
scala> Some(1) orElse None
res0: Option[Int] = Some(1)
scala> None orElse Some(1)
res1: Option[Int] = Some(1)
scala> None orElse None
res2: Option[Nothing] = None
scala> Some(1) orElse Some(2)
res3: Option[Int] = Some(1)
It's not hard to do it by hand:
scala> val a = Some(1)
a: Some[Int] = Some(1)
scala> val b = Some(2)
b: Some[Int] = Some(2)
scala> Seq(a,b).flatten.headOption
res0: Option[Int] = Some(1)
In the question comments, you mention you can't have Some(a) and Some(b), so what you really have is Option[Either[Int,Int]]. In that case, you can use x.map(_.merge) to get back to Option[Int], eg
scala> val x:Option[Either[Int,Int]] = Some(Left(2))
x: Option[Either[Int,Int]] = Some(Left(2))
scala> x.map(_.merge)
res0: Option[Int] = Some(2)
When I'm matching value of case classes, such as:
sealed abstract class Op
case class UOp[T, K](f: T => K) extends Op
case class BOp[T, Z, K](f: (T, Z) => K) extends Op
like this:
def f(op: Op): Int =
op match
{
case BOp(g) => g(1,2)
case UOp(g) => g(0)
}
the compiler infers it as
val g: (Nothing, Nothing) => Any
val g: Nothing => Any
Why am I getting Nothing as the type? Is it because of JVM type erasure? Are there elegant ways to match functions against variables?
I came up with this "hackish" solution, maybe there are other ways or cleaner ways to do this still without relying on reflection.
Define a few partial functions which will handle various args:
scala> val f: PartialFunction[Any, String] = { case (x: Int, y: String) => y * x }
f: PartialFunction[Any,String] = <function1>
scala> val g: PartialFunction[Any, String] = { case x: Int => x.toString }
g: PartialFunction[Any,String] = <function1>
scala> def h: PartialFunction[Any, BigDecimal] = { case (a: Int, b: Double, c: Long) => BigDecimal(a) + b + c }
h: PartialFunction[Any,BigDecimal]
scala> val l: List[PartialFunction[Any, Any]] = f :: g :: h :: Nil
l: List[PartialFunction[Any,Any]] = List(<function1>, <function1>, <function1>)
Check which functions can handle different inputs:
scala> l.map(_.isDefinedAt(1))
res0: List[Boolean] = List(false, true, false)
scala> l.map(_.isDefinedAt((1, "one")))
res1: List[Boolean] = List(true, false, false)
Given input find and apply a function:
scala> def applyFunction(input: Any): Option[Any] = {
| l find (_.isDefinedAt(input)) map (_ (input))
| }
applyFunction: (input: Any)Option[Any]
scala> applyFunction(1)
res1: Option[Any] = Some(1)
scala> applyFunction((2, "one"))
res2: Option[Any] = Some(oneone)
scala> applyFunction("one")
res3: Option[Any] = None
scala> applyFunction(1, 1.1, 9L)
res10: Option[Any] = Some(11.1)
This looks quite type unsafe and there must be better ways to do this.
I think magnet pattern should handle this well in more typesafe manner.
As an example, I want to apply a function f: (Int,Int) => Int to two elements of type Option[Int]. My thoughts were something like (a,b).zipped.map(f), but this yields a List, and I want to get a new Option[Int] as result.
scala> def f(a:Int,b:Int) = a*b
f: (a: Int, b: Int)Int
scala> val x = Some(42)
x: Some[Int] = Some(42)
scala> val y:Option[Int] = None
y: Option[Int] = None
scala> (x,y).zipped.map(f)//I want None as a result here
res7: Iterable[Int] = List()
How can this be done without explicitly branching?
Just like many other operations in scala this can be done via for comprehension:
def f(a:Int,b:Int) = a*b
for (x <- maybeX; y <- maybeY) yield f(x, y)
As often is the case with this type of question, scalaz has some help:
scala> import scalaz._
import scalaz._
scala> import Scalaz._
import Scalaz._
scala> def f(a:Int,b:Int) = a*b
f: (a: Int, b: Int)Int
scala> val x = Some(42)
x: Some[Int] = Some(42)
scala> val y:Option[Int] = None
y: Option[Int] = None
scala> ^(x,y)(f)
res0: Option[Int] = None
scala> val x = 42.some
x: Option[Int] = Some(42)
scala> (x |#| y)(f)
res3: Option[Int] = None
Using om-nom-nom's idea, I can do something like this:
scala> def f(a:Int,b:Int) = a*b
f: (a: Int, b: Int)Int
scala> def lifted(f: (Int,Int) => Int) = (a:Option[Int],b:Option[Int]) => for(x<-a;y<-b) yield f(x,y)
lifted: (f: (Int, Int) => Int)(Option[Int], Option[Int]) => Option[Int]
scala> def liftedF = lifted(f)
liftedF: (Option[Int], Option[Int]) => Option[Int]
scala> val x = Some(42)
x: Some[Int] = Some(42)
scala> val y:Option[Int] = None
y: Option[Int] = None
scala> liftedF(x,x)
res0: Option[Int] = Some(1764)
scala> liftedF(x,y)
res2: Option[Int] = None
We can even generalize this...please cover your eyes:
def lift2[A, B, C](f: (A, B) => C): (Option[A], Option[B]) => Option[C] = (a: Option[A], b: Option[B]) =>
for (x <- a; y <- b) yield f(x, y)
Is there a predefined function x in Scala that combine 2 Options so that
Some(a) x None => Some(a)
None x Some(b) => Some(b)
None x None => None
Yes, this is the orElse method. It chooses the first defined value, or None if neither is defined.
scala> Some(1) orElse None
res0: Option[Int] = Some(1)
scala> None orElse Some(1)
res1: Option[Int] = Some(1)
scala> None orElse None
res2: Option[Nothing] = None
scala> Some(1) orElse Some(2)
res3: Option[Int] = Some(1)
It's not hard to do it by hand:
scala> val a = Some(1)
a: Some[Int] = Some(1)
scala> val b = Some(2)
b: Some[Int] = Some(2)
scala> Seq(a,b).flatten.headOption
res0: Option[Int] = Some(1)
In the question comments, you mention you can't have Some(a) and Some(b), so what you really have is Option[Either[Int,Int]]. In that case, you can use x.map(_.merge) to get back to Option[Int], eg
scala> val x:Option[Either[Int,Int]] = Some(Left(2))
x: Option[Either[Int,Int]] = Some(Left(2))
scala> x.map(_.merge)
res0: Option[Int] = Some(2)
How would you find minValue below?
I have my own solution but want to see how others would do it.
val i1: Option[Int] = ...
val i2: Option[Int] = ...
val defaultValue: Int = ...
val minValue = ?
Update: I just noticed that my solution below and the one in your answer behave differently—I read your question as asking for the minimum of the two values when there are two values, but in your answer you're effectively treating None as if it contained a value that's either bigger (for min) or smaller (for max) than anything else.
To be more concrete: if i1 is Some(1) and i2 is None, my solution will return the default value, while yours will return 1.
If you want the latter behavior, you can use the default semigroup instance for Option[A] and the tropical semigroup for Int. In Scalaz 7, for example, you'd write:
import scalaz._, Scalaz._
optionMonoid(Semigroup.minSemigroup[Int]).append(i1, i2) getOrElse defaultValue
Or the following shorthand:
Tags.Min(i1) |+| Tags.Min(i2) getOrElse defaultValue
It's not as clean as the applicative functor solution below, but if that's your problem, that's your problem.
Here's a more idiomatic way that doesn't involve creating an extra list:
(for { x <- i1; y <- i2 } yield math.min(x, y)) getOrElse defaultValue
Or, equivalently:
i1.flatMap(x => i2.map(math.min(x, _))) getOrElse defaultValue
What you're doing is "lifting" a two-place function (min) into an applicative functor (Option). Scalaz makes this easy with its applicative builder syntax:
import scalaz._, Scalaz._
(i1 |#| i2)(math.min) getOrElse defaultValue
The standard library solution isn't much less elegant in this case, but this is a useful abstraction to know about.
I solved a similar problem using the following approach. We handle a special case when both of the options have values, otherwise we use an API method Option.orElse.
val a: Option[Int] = Some(10)
val b: Option[Int] = Some(20)
val c: Option[Int] = (a, b) match {
case (Some(x), Some(y)) => Some(x min y)
case (x, y) => x orElse y
}
I think this is what you're after:
val minValue = List(i1, i2).flatten match {
case Nil => defaultValue
case xs => xs.min
}
I'd avoid sorted since sorting requires a lot more processing than simply finding the max or min (although it probably doesn't make much difference in this case).
val minValue: Int = List(i1, i2).flatten.sorted.headOption getOrElse defaultValue
You can use patterns in for expressions, values that do not match the pattern are discarded.
(for (Some(x) <- List(None, Some(3))) yield x) max
Not as good as the List.flatten approach though.
Another option which wasn't mentioned is using reduceLeftOption (interchange math.max and math.min as desired):
val min = (first ++ second).reduceLeftOption(math.min).getOrElse(defaultValue)
scala> val first = Some(10)
first: Some[Int] = Some(10)
scala> val second: Option[Int] = None
second: Option[Int] = None
scala> val defaultMin = -1
defaultMin: Int = -1
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res7: Int = 10
scala> val first: Option[Int] = None
first: Option[Int] = None
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res8: Int = -1
scala> val first = Some(10)
first: Some[Int] = Some(10)
scala> val second = Some(42)
second: Some[Int] = Some(42)
scala> (first ++ second).reduceLeftOption(math.min).getOrElse(defaultMin)
res9: Int = 10
We can combine the 2 Options as an Iterable with Option's ++ operator, which allows us to use minOption (to nicely handle the case of the empty iterable formed by the None/None case) and fallback on a default value if necessary with getOrElse:
(optionA ++ optionB).minOption.getOrElse(-1)
// None and None => -1
// Some(5) and None => 5
// None and Some(5) => 5
// Some(5) and Some(3) => 3
If you want to avoid using scalaz and map/for/getOrElse, you can do the following:
val minValue = (i1, i2) match {
case (Some(x), Some(y)) => math.min(x, y)
case _ => defaultValue
}
tl;dr
You can do that you need elegant using custom cats Semigroup instances:
import cats.kernel.Semigroup
import cats.instances.option._ // this import is for cats std option combiner
import cats.syntax.semigroup._
object Implicits {
implicit val intMinSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.min(x, y)
implicit val intMaxSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.max(x, y)
}
import Implicits.intMinSemigroup
// these are results for minSemigroup
// List((Some(1),Some(1),Some(2)), (Some(1),Some(1),None), (None,Some(2),Some(2)), (None,None,None))
//import Implicits.intMaxSemigroup
// these are results for maxSemigroup
// List((Some(1),Some(2),Some(2)), (Some(1),Some(1),None), (None,Some(2),Some(2)), (None,None,None))
for {
maybeA <- Seq(Some(1), None)
maybeB <- Seq(Some(2), None)
} yield (maybeA, maybeA |+| maybeB, maybeB)
if you want replace None by default value you can use combine twice:
val defaultValue: Int = 3
val optionMin = for {
maybeA <- Seq(Some(1), None)
maybeB <- Seq(Some(2), None)
} yield (maybeA |+| maybeB) |+| Some(defaultValue)
// List(Some(1), Some(1), Some(2), Some(3))
How it works
Shortly, Semigroup[A] is typeclass for combining two values of the same type A into the one value of type A.
Here we use std cats OptionMonoid (it extends Semigroup[Option[A]]) here source code:
class OptionMonoid[A](implicit A: Semigroup[A]) extends Monoid[Option[A]] {
def empty: Option[A] = None
def combine(x: Option[A], y: Option[A]): Option[A] =
x match {
case None => y
case Some(a) =>
y match {
case None => x
case Some(b) => Some(A.combine(a, b))
}
}
}
We see that it takes option matching on his own and everything what we should give him to work is implicit A: Semigroup[A]. In our case we write two different combiners for min, max cases:
object Implicits {
implicit val intMinSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.min(x, y)
implicit val intMaxSemigroup: Semigroup[Int] =
(x: Int, y: Int) => math.max(x, y)
}
So, we import combiners (i.e. import Implicits.intMinSemigroup) and just use cats.syntax.semigroup for using combine function as operator |+|:
maybeA |+| maybeB.
In conclusion, you can just define your custom semigroup for any type (not only Int) and combine options of this type after importing some cats syntax and instances.