Scala:要么,右,左

Scala: Either, Right, Left

我是 Scala 的新手,目前正在尝试使用 play 框架。

这是我写的工作代码:

def authenticate = Action (BodyParsers.parse.json) { req =>
    req.body.validate[AuthenticationForm].map {form =>
        UserRepository.findByCredentials(form).map { user =>
            user.apiKeys.find(_.deviceId == form.deviceId).map { apiKey =>
                Ok(Json.toJson(apiKey))
            }.getOrElse({
                // HOW DO I TRANSFORM THIS INTO MORE BEAUTIFUL CODE
                val createdApiKey = ApiKeyRepository.create(new ApiKey(form.deviceId, form.deviceId))
                val userToWithNewApiKey = user.copy(apiKeys = user.apiKeys.:+(createdApiKey))
                UserRepository.update(userToWithNewApiKey)

                Ok(Json.toJson(createdApiKey))
            })
        }.getOrElse {
            Unauthorized
        }
    }.getOrElse {
        BadRequest
    }
}

嗯,这看起来不太好。我们能做得更好吗?我还不能。但是我看到了这个 Whosebug post: 看起来很不错 :)

现在我想知道如何转换我的代码,使其看起来像给定的示例。当然我已经试过了,但我无法编译,也不知道如何处理注释后的代码("HOW DO I TRANSFORM THIS INTO MORE BEAUTIFUL CODE")。 在我的例子中,我使用 play.api.mvc.Result 而不是上面 link 中给出的 "Failure"。那么我的 Either[play.api.mvc.Result, ?WhatHere?] 应该是什么类型的呢?

此致

编辑: 我接受了特拉维斯的回答。非常感谢。

对于任何感兴趣的人来说,我可以编写更好看的代码,感谢 Travis:

def getApiKey(user: User, deviceId: String) : ApiKey = {
  user.apiKeys.find(_.deviceId == deviceId).getOrElse {
    val createdApiKey =
      ApiKeyRepository.create(new ApiKey(deviceId, deviceId))

    val userToWithNewApiKey =
      user.copy(apiKeys = user.apiKeys.:+(createdApiKey))

    UserRepository.update(userToWithNewApiKey)
    createdApiKey
  }
}

def authenticate = Action (BodyParsers.parse.json) { req =>
  (for {
    form <- req.body.validate[AuthenticationForm].asOpt.toRight(BadRequest).right
    user <- UserRepository.findByCredentials(form).toRight(Unauthorized).right
  } yield {
      Ok(Json.toJson(getApiKey(user, form.deviceId)))
  }).merge
}

这是快速且未经测试的,但应该是一个不错的开始。首先,您可以通过使用 toRight 来折叠一些嵌套以获得 Either[Status, ?]Either 不是一元的,但它的正确投影(我们可以使用 .right 得到)是。一旦不再可能失败,我们就使用 yield 来处理结果。我已经稍微重写了您的 apiKey 内容以避免重复 Ok(Json.toJson(key)) 部分。

def authenticate = Action (BodyParsers.parse.json) { req =>
  for {
    form <- req.body.asOpt.toRight[Status](BadRequest).right
    user <- UserRepository.findByCredentials(form).toRight[Status](
      Unauthorized
    ).right
  } yield {
    val apiKey = user.apiKeys.find(_.deviceId == form.deviceId).getOrElse {
      val createdApiKey =
        ApiKeyRepository.create(new ApiKey(form.deviceId, form.deviceId))

      val userToWithNewApiKey =
        user.copy(apiKeys = user.apiKeys.:+(createdApiKey))

      UserRepository.update(userToWithNewApiKey)
      createdApiKey
    }
    Ok(Json.toJson(apiKey)): Status
  }.e.merge
}

for-理解的最终结果(即除 .e.merge 之外的所有内容)是 RightProjection[Status, Status]。我们将其转换回带有 .e 的普通旧 Either[Status, Status]。此时我们不再需要跟踪失败和成功之间的区别,所以我们将整个事情转换为 Status.merge.