[英]Better way of wrapping the result of a monadic-for in an Option, in Scala
I want to use Scala's "monadic-for" to obtain, at the end, either Some(x) or None. 我想使用Scala的“ monadic-for”最后获取Some(x)或None。
This means, that the first element in this monad has to be an Option. 这意味着此monad中的第一个元素必须是Option。
The problem is, that sometimes I don't have an Option to start with. 问题是,有时我没有选择权。
So I "fake" one, using a dummy value. 因此,我使用伪值“伪造”一个。
The abstract form looks like this: 抽象形式如下:
for {
// only used to yield Some/None at the end
dummyVal <- Some("dummy value")
// ...
// ... other monadic expressions
// ...
} yield {
// result which will be wrapped into Some(...)
}
A concrete example could be: 一个具体的例子可能是:
case class Person(name:String, age:Int)
val p = Person("John", 32)
At the end this evaluates to Some("John")
: 最后,结果为
Some("John")
:
for {
dummy <- Some("dummy")
matchedPerson = p
if matchedPerson.age > 30
} yield {
matchedPerson.name
}
Whereas this evaluates to None
: 而结果为
None
:
for {
dummy <- Some("dummy")
matchedPerson = p
if matchedPerson.age > 55
} yield {
matchedPerson.name
}
Though I get what I want (a monadic-for that evaluates to Option
), I have a bad feeling.I had to "misuse" the monadic-for by starting with a "dummy" Option
, just for the sake of getting Some
/ None
at the end. 尽管我得到了想要的东西(一个monadic-for的结果表示为
Option
),但我还是有一种不好的感觉。我不得不以一个“虚拟” Option
开头“滥用” monadic-for,仅是为了获得Some
/ None
。
My question is: is there a better way to achieve this, without creating a dummy value? 我的问题是:是否有更好的方法来实现这一目标,而又不创建虚拟值?
UPDATE: 更新:
Consider this example, which builds on the one above: 考虑以下示例,该示例基于以上示例:
case class Person(name: String, username: String, age: Int)
case class Session(loggedInPerson: Person)
def isValidUser(username: String):Boolean = ???
def isValidPassword(username: String, password: String):Boolean = ???
def readPassword():Option[String] = ???
val usr = Person("John Wayne", "jwayne", 55)
for {
dummy <- Some("does not matter")
username = usr.username
if isValidUser(username)
pass <- readPassword()
if isValidPassword(username, pass)
} yield {
Session(usr)
}
Now it's clear that the "for" is pretty long and probably not practicable to re-write using filters, flatMaps, etc. 现在很明显,“ for”很长,使用过滤器,flatMap等进行重写可能不可行。
My goal at the end is to get an Option[Session]
. 我最终的目标是获得
Option[Session]
。 I "force" the for to this outcome by starting with a dummy Some
value. 我从一个虚拟的
Some
值开始“强制”此结果。
But as has been pointed out by Noel M
, I can just rewrite: 但是正如
Noel M
所指出的,我可以重写一下:
dummy <- Some("does not matter")
with: 与:
usr <- Some(usr)
Can't you just do: 你不能只是做:
for {
person <- Some(p)
if person.age > 55
} yield person.name
? ?
Though I feel I've misunderstood your question. 虽然我觉得我误解了您的问题。
Alternatively you could use Scalaz : 或者,您可以使用Scalaz :
scala> import scalaz._, Scalaz._
import scalaz._
import Scalaz._
scala> case class Person(name:String, age:Int)
defined class Person
scala> val p = Person("John", 32)
p: Person = Person(John,32)
scala> (p.age > 55) option p.name
res0: Option[String] = None
scala> (p.age > 30) option p.name
res1: Option[String] = Some(John)
You can mix for-comprehension, option-boxing and pattern-matching: 您可以混合理解,选项装箱和模式匹配:
scala> case class Person(name: String, age:Int)
defined class Person
scala> val p = Person("John", 32)
p: Person = Person(John,32)
scala> for { Person(name, age) <- Option(p); if age > 30} yield name
res0: Option[String] = Some(John)
scala> for { Person(name, age) <- Option(p); if age > 55} yield name
res1: Option[String] = None
Scalaz
has a nice approach as @Noel mentioned, but using pure scala you can use: Scalaz
有一个不错的方法,如@Noel所述,但是使用纯scala可以使用:
Option(p).filter( _.age > 55).map(_.name) // None
Option(p).filter( _.age > 30).map(_.name) // Some(john)
or, all in one: 或者,合而为一:
Option(p).collectFirst{ case Person(name, age) if age > 55 => name}
It has the advantage that'll also work when p
is null
. 它的优点是当
p
为null
时也可以使用。
I'm not sure I understand this question. 我不确定我是否理解这个问题。 Does this help ?
这有帮助吗?
scala> case class Person(name: String, age:Int)
defined class Person
scala> val p = Person("John", 32)
p: Person = Person(John,32)
scala> PartialFunction.condOpt(p){case Person(name, age) if age > 55 => name}
res0: Option[String] = None
scala> PartialFunction.condOpt(p){case Person(name, age) if age > 30 => name}
res1: Option[String] = Some(John)
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.