开发者

for..else for Option types in Scala?

开发者 https://www.devze.com 2023-03-24 18:39 出处:网络
Suppose I have two Options and, if both are Some, execute one code path, and if note, execute another.I\'d like to do something like开发者_如何学Python

Suppose I have two Options and, if both are Some, execute one code path, and if note, execute another. I'd like to do something like开发者_如何学Python

for (x <- xMaybe; y <- yMaybe) {
  // do something
}
else {
  // either x or y were None, handle this
}

Outside of if statements or pattern matching (which might not scale if I had more than two options), is there a better way of handling this?


Very close to your syntax proposal by using yield to wrap the for output in an Option:

val result = { 
  for (x <- xMaybe; y <- yMaybe) yield {
    // do something
  }
} getOrElse {
  // either x or y were None, handle this
}

The getOrElse block is executed only if one or both options are None.


You could pattern match both Options at the same time:

(xMaybe, yMaybe) match {
  case (Some(x), Some(y)) => "x and y are there"
  case _ => "x and/or y were None"
}


The traverse function in Scalaz generalises your problem here. It takes two arguments:

  1. T[F[A]]
  2. A => F[B]

and returns F[T[B]]. The T is any traversable data structure such as List and the F is any applicative functor such as Option. Therefore, to specialise, your desired function has this type:

  • List[Option[A]] => (A => Option[B]) => Option[List[B]]

So put all your Option values in a List

  • val z = List(xMaybe, yMaybe)

Construct the function got however you want to collection the results:

  • val f: X => Option[Y] = ...

and call traverse

  • val r = z traverse f

This programming patterns occurs very often. It has a paper that talks all about it, The Essence of the Iterator Pattern.

note: I just wanted to fix the URL but the CLEVER edit help tells me I need to change at least 6 characters so I include this useful link too (scala examples):
http://etorreborre.blogspot.com/2011/06/essence-of-iterator-pattern.html


Why would something like this not work?

val opts = List[Option[Int]](Some(1), None, Some(2))
if (opts contains None) {
  // Has a None
} else {
  // Launch the missiles
  val values = opts.map(_.get) // We know that there is no None in the list so get will not throw
}


If you don't know the number of values you are dealing with, then Tony's answer is the best. If you do know the number of values you are dealing with then I would suggest using an applicative functor.

((xMaybe |@| yMaybe) { (x, y) => /* do something */ }).getOrElse(/* something else */)


You said you want the solution to be scalable:

val optional = List(Some(4), Some(3), None)

if(optional forall {_.isDefined}) {
    //All defined
} else {
    //At least one not defined
}

EDIT: Just saw that Emil Ivanov's solution is a bit more elegant.


Starting Scala 2.13, we can alternatively use Option#zip which concatenates two options to Some tuple of their values if both options are defined or else None:

opt1 zip opt2 match {
  case Some((x, y)) => "x and y are there"
  case None         => "x and/or y were None"
}

Or with Option#fold:

(opt1 zip opt2).fold("x and/or y were None"){ case (x, y) => "x and y are there" }


For scaling to many options, try something along these lines:

 def runIfAllSome[A](func:(A)=>Unit, opts:Option[A]*) = {
   if(opts.find((o)=>o==None) == None) for(opt<-opts) func(opt.get)
 }

With this, you can do:

scala> def fun(i:Int) = println(i)
fun: (i: Int)Unit

scala> runIfAllSome(fun, Some(1), Some(2))
1
2

scala> runIfAllSome(fun, None, Some(1))

scala>


I think the key point here is to think in term of types as what you want to do. As I understand it you want to iterate over a list of Option pairs and then do something based on a certain condition. So the interesting bit of your question would be , what would the return type look like you would except? I think it would look something like this: Either[List[Option], List [Option,Option]] . on the error side (left) you would accumulate the option which was paired with a None (and was left alone so to speak) . On the right side you sum the non empty options which represent your successful values. So we would just need a function which does exactly that. Validate each pair and accumulate it according to it's result( success - failure) . I hope this helps , if not please explain in more detail your usecase. Some links to implement what I described : http://applicative-errors-scala.googlecode.com/svn/artifacts/0.6/pdf/index.pdf and : http://blog.tmorris.net/automated-validation-with-applicatives-and-semigroups-for-sanjiv/

0

精彩评论

暂无评论...
验证码 换一张
取 消