简体   繁体   English

何时使用Option / None返回类型

[英]When to use Option/None return types

I understand the whole principle and concept behind the Some/None/Option and I can certainly appreciate its merits. 我理解Some / None / Option背后的整个原理和概念,我当然可以理解它的优点。 My question is more of best practice. 我的问题更多是最佳做法。 When does it become overkill and when does it make sense to use it religiously. 什么时候变得矫枉过正,什么时候有意义地使用它。 I think (and i could be wrong) but it makes sense to use it as much as possible since it's a safer way to pass around nothing (as opposed to null). 我认为(我可能是错的)但是尽可能多地使用它是有道理的,因为它是一种更安全的方式来传递任何东西(而不是null)。 The thing I see myself doing a lot though is having some functions littered with map, getOrElse, get, match and even sometimes nesting them which tends to look ugly. 我看到自己做了很多事情,虽然有一些功能乱七八糟的地图,getOrElse,get,match甚至有时嵌套它们往往看起来很难看。 Is there some concept I'm missing or some best practice to do with a function that receives multiple Optional values. 是否有一些我缺少的概念或一些最佳实践与接收多个Optional值的函数有关。 For example: 例如:

  def updateJobs(id: Int) = withAuth {
    request => {

      User.userWithToken(request.headers.get("token").get).map {
        user =>
          Job.jobsAfterIdForForeman(id.toString, user.id.toString) match {
            case Some(json) => Ok(json)
            case _ => NoContent
          }
      }.getOrElse(BadRequest)
    }
  }

or even worse for example: 甚至更糟的例如:

  def addPurchaseRequest(json: JsValue) = {
    (json \ "jobId").asOpt[Long].map {
      jobId => JobDAO.jobWithId(jobId).map {
        job => PurchaseRequestDAO.insert(new PurchaseRequest(json, job)).map {
          model =>
            val request = model.asInstanceOf[PurchaseRequest]
            (json \ "items").asOpt[List[JsObject]].map {
              list => {
                if (PurchaseItemAssociationDAO.bulkInsert(PurchaseItemAssociation.itemsFromJsonArray(list, request.id))) Option(request.addResponseJson) else None
              }
            }.getOrElse(None)
        }.getOrElse(None)
      }.getOrElse(None)
    }.getOrElse(None)
  }

I managed to refactor some to not look so crazy, but is there a better way to refactor this so it doesn't look so crazy? 我设法重构一些看起来不那么疯狂,但有没有更好的方法来重构这个看起来不那么疯狂? Am I missing something or do you get used to things just looking like this? 我错过了什么或你是否习惯了这样的事情? Seems like there should certainly be a cleaner practice. 似乎应该有一个更清洁的做法。

Since the Option class is monadic, you should use for comprehensions to make that code look cleaner. 由于期权类是一元,你应该使用for内涵,使代码看起来更干净。 For example, your second example can be rewritten as: 例如,您的第二个示例可以重写为:

def addPurchaseRequest(json: JsValue) = 
  for {
    jobId <- (json \ "jobId").asOpt[Long]
    job <- JobDAO.jobWithId(jobId)
    model <- PurchaseRequestDAO.insert(new PurchaseRequest(json, job))
    request = model.asInstanceOf[PurchaseRequest]
    list <- (json \ "items").asOpt[List[JsObject]]
      if PurchaseItemAssociationDAO.bulkInsert(PurchaseItemAssociation.itemsFromJsonArray(list, request.id))
  } yield request.addResponseJson

Use for-comprehensions or at least use flatMap instead of map / getOrElse(None) to make things more compact. 使用for-comprehensions或至少使用flatMap而不是map / getOrElse(None)来使事情更紧凑。 Also, it's customary to put the new variable at the end of the previous line instead of on its own line. 此外,习惯上将新变量放在前一行的末尾而不是它自己的行。 This would clean up your worst case tremendously. 这将极大地清理你的最坏情况。

Alternatively, with particularly long chains that require intermediate logic, I find an exception-like mechanism works even better than for-comprehensions (based on the same principle as nonlocal returns): 或者,对于需要中间逻辑的特别长的链,我发现类似异常的机制比for-comprehensions更好(基于与非本地返回相同的原理):

trait Argh extends scala.util.control.ControlThrowable
implicit class GetOrArgh[A](val underlying: Option[A]) extends AnyVal {
  def or(a: Argh) = underlying match {
    case None => throw a
    case _ => underlying.get
  }
}
def winnow[A](f: Argh => A): Option[A] = {
  val argh = new Argh { }
  try { Some(f(argh)) }
  catch { case x: Argh if (x eq argh) => None }
}

Which you then use like this: 你然后使用这样:

def addPurchaseRequest(json: JsValue) = winnow { fail =>
  val jobId = (json \ "jobId").asOpt[Long] or fail
  val job = JobDAO.jobWithId(jobId) or fail
  val model = PurchaseRequestDAO.insert(new PurchaseRequest(json, job)) or fail
  val request = model match {
    case pr: PurchaseRequest => pr
    case _ => throw fail
  }
  val list = (json \ "items").asOpt[List[JsObject]]
  if (!PurchaseItemAssociationDAO.bulkInsert(
    PurchaseItemAssociation.itemsFromJsonArray(list, request.id)
  )) throw fail
  request.addResponseJson
}

Whether this approach or the for comprehension works better depends (in my experience) on how much intermediate processing you have to do. 这种方法或理解是否更好地取决于(根据我的经验)你需要做多少中间处理。 If you can make everything one-liners, the for-comprehension is nice and clean. 如果你能把所有东西都做成单行,那么理解就是美好而干净。 Once you require something a little more tangled, I like this approach better. 一旦你需要更纠结的东西,我更喜欢这种方法。

Note that for comprehensions are canonical Scala constructs, whereas this will require some learning for new users. 请注意,对于理解是规范的Scala构造,而这需要一些新用户的学习。 Thus, favor for comprehensions or flatMaps in code that people may need to get up to speed on rapidly. 因此,有利于人们可能需要快速加速的代码中的理解或flatMaps。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 什么时候应该使用Option.empty [A]什么时候我应该在Scala中使用None? - When should I use Option.empty[A] and when should I use None in Scala? 如果Option不包含任何内容或包含不需要的类型,则通常返回None - Idiomatically return None if Option contains None or contains unwanted type 如何过滤从列表[(String,Option [Int])]删除None类型? - How to filter remove None types from List[(String , Option[Int])]? 将 Option、Some 和 None 密封类型重命名为特定于域的语言? - Renaming the Option, Some, and None sealed types to a domain-specific language? 如何模拟没有返回值的方法是Option [SomeCaseClassDefinedInsideThisClass]的方法的返回 - How to mock return of None of method which return type is Option[SomeCaseClassDefinedInsideThisClass] 如何知道何时使用PartialFunction vs return Option - How to know when to use PartialFunction vs return Option 当有意义的值为None时Option的惯用语法 - Idiomatic syntax for Option when the meaningful value is None 初始化scala Option时,“ Package None不是值” - “Package None is not a value” when initializing a scala Option 在Scala上使用带有选项返回类型的地图 - Using map on Scala with option return types 如果我传入一个选项,Scala for-yield是否可以返回? - Can a Scala for-yield return None if I pass in an Option to it?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM