使用Try for flow控制是一种好习惯吗?

时间:2017-05-02 14:54:36

标签: scala

我们的代码库中有一个函数,其签名如下:

def hasPermission(...): Try[Unit]

它基本上检查用户是否有权对某个项目执行某项操作。如果用户具有权限,则返回空Success,如果没有,则返回具有特定异常类型的Failure。这个功能经常在这样的理解中使用:

for {
    _ <- hasPermission(...)
    res <- doSomething()
} yield res

这似乎是不好的做法,但我不能清楚地阐明为什么我有这种感觉。对我而言,似乎hasPermission应该只返回Boolean

这适用于Try吗?

编辑:我认为我的问题与链接的问题不同,因为它更具体。那个问题是关于返回Try [Unit]的一般性问题,我认为在某些情况下这是可以接受的。

2 个答案:

答案 0 :(得分:2)

如果方法显示hasPermission,那么我会说它应该返回BooleanTry[Boolean]Try[Unit]不像Try[Boolean]那么明显,调用者必须检查异常以判断它是否没有权限,或者是否无法检索权限信息。

现在说,通常调用hasPermission然后根据结果行事可能会导致竞争条件(例如,如果在调用hasPermission后撤消权限)。因此,通常最好是def doSomething(...): Try[Unit],然后举起例如NoPermissionException

答案 1 :(得分:0)

  

Generally, the use of exceptions for control flow is an anti-pattern

Try尝试(没有双关语)封装该流量控件,但如果您实际上需要使用异常,则没有理由这样做。 Scala 2.12的Either实现似乎接近您可能想要的内容:

  

要么是偏右的,这意味着Right被认为是操作的默认情况。如果是Left,则mapflatMap等操作会使Left值保持不变:

假设您正在实施Web服务器,并且此逻辑控制着特定路径:

type Request = ...
type Response = String
type ResponseOr[+T] = Either[Response, T]

def checkPermission: ResponseOr[Unit] = 
  if(hasPermission) Right(())
  else Left("insufficient permissions")

def doSomething(req: Request): ResponseOr[Something] = 
  if(argumentsAreBad(req)) Left("arguments are bad!")
  else Right(new Something)

def makeResponse(result: Something): Response = ???

def handleIt(req: Request): Response = {
  val result = for {
    _ <- checkPermission
    result <- doSomething
  } yield makeResponse(result)
  result.merge // special method for `Either[T, T]` that gives a `T`
}

您会看到与Success vs Failure类似的行为 - 将Left视为与Failure类似,如果在任何时候,其中一个是flatMap / map步骤返回Left,这是最终结果,其余的是&#34;跳过&#34;。无需例外。