斯卡拉:左右,右,左

时间:2015-04-21 16:33:00

标签: scala functional-programming playframework-2.0 monads either

我是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
    }
}

嗯,这看起来不太好看。我们可以做得更好吗?我还不能。但是我看到了这个stackoverflow帖子:https://stackoverflow.com/a/24085333/3038183看起来很不错:)。

现在我想知道如何转换我的代码,所以它在给定的例子中看起来像。当然我已经尝试了,但我无法编译,也不知道如何在评论后处理代码(“我如何将其转换为更美丽的代码”)。 在我的情况下,我使用play.api.mvc.Result而不是上面的链接中给出的“失败”。那么我的Either [play.api.mvc.Result,?WhatHere?]应该是什么类型的?

祝你好运

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

对于任何感兴趣的人来说,感谢特拉维斯,我能写得更好看的代码:

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
}

1 个答案:

答案 0 :(得分:3)

这是快速且未经测试但应该是一个不错的开始。首先,您可以使用toRight获取Either[Status, ?]来折叠部分嵌套。 Either不是monadic,但它的正确投影(我们可以使用.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]。我们使用Either[Status, Status]将其转换回普通的.e。此时,我们不再需要跟踪失败和成功之间的区别,因此我们将整个内容转换为Status .merge