2015-04-29 4 views
0

эта тема дали мне представление о том, как структурировать код: Scala-way to handle conditions in for-comprehensions?Scala - Фьючерсы и Eithers

детали в вопросе:

// First create the JSON 
val resultFuture: Future[Either[Failure, JsResult]] = for { 
    userRes <- userDao.findUser(userId) 
    user <- userRes.withFailure(UserNotFound).right 
    authRes <- userDao.authenticate(user) 
    auth <- authRes.withFailure(NotAuthenticated).right 
    goodRes <- goodDao.findGood(goodId) 
    good <- goodRes.withFailure(GoodNotFound).right 
    checkedGood <- checkGood(user, good).right 
} yield renderJson(Map("success" -> true))) 

Это те строки, я не понимаю:

user <- userRes.withFailure(UserNotFound).right 
authRes <- userDao.authenticate(user) 

userRes.withFailure (UserNotFound) .right сопоставлен с userDao.authenticate (пользователь). Это приведет к созданию нового Либо с Будущим справа, правильно?

Как

val resultFuture: Future[Either[Failure, JsResult]] 

может быть его типа. Я думаю, вместо JsResult должно быть другое будущее. Может ли кто-нибудь объяснить это мне?

EDIT: С cmbaxter и Arne Claassen подтвердил это, новый вопрос: Как я должен написать этот код, так что это не выглядит некрасиво, но чистый и структурированный?

+0

Я согласен, что с этим ответом что-то не так. Как много раз было опубликовано на SO, вы не можете смешивать типы для понимания. Какой бы тип, с которого вы начинаете, - это тип, который вам нужно распространять с помощью остальных шагов ('flatMap's). В этом случае, поскольку 'findUser' возвращает' Future [Option [User]], '' 'Future' становится типом, который вы должны продолжать использовать в остальных шагах. Вы не можете просто переключиться на «Либо», как этот человек делает на втором этапе понимания. – cmbaxter

+0

Немного смутил себя. Обычно для понимания работает один тип Монады за раз, и здесь мы смешиваем функции, которые производят «Будущие» и «Либо». Я не смог получить исходный пример для разбора –

ответ

4

Я считаю, что ответ, который вы получили бесполезно, смешался с Either, когда Future 's уже отлично способны сообщить об ошибке. Главное, что вам не хватало, это способ получить от Option значение параметра без явного исключения исключений.

Я хотел бы предложить вам изменить Отказы объекта к следующему:

object Failures { 

    sealed trait Failure extends Exception 

    // Four types of possible failures here 
    case object UserNotFound extends Failure 

    case object NotAuthenticated extends Failure 

    case object GoodNotFound extends Failure 

    case object NoOwnership extends Failure 

    // Put other errors here... 

    // Converts options into Futures 
    implicit class opt2future[A](opt: Option[A]) { 
    def withFailure(f: Failure) = opt match { 
     case None => Future.failed(f) 
     case Some(x) => Future.successful(x) 
    } 
    } 
} 

Теперь вы можете отобразить Future[Option[A]] в Future[A] и указать состояние ошибки, в результате чего для понимания, как это:

def checkGood(user: User, good: Good) = 
    if (checkOwnership(user, good)) 
    Future.successful(good) 
    else 
    Future.failed(NoOwnership) 

val resultFuture: Future[JsResult] = for { 
    userOpt <- userDao.findUser(userId) 
    user <- userOpt.withFailure(UserNotFound) 
    authOpt <- userDao.authenticate(user) 
    auth <- authOpt.withFailure(NotAuthenticated) 
    goodOpt <- goodRes.withFailure(GoodNotFound) 
    checkedGood <- checkGood(user, good) 
} yield renderJson(Map("success" -> true)))) 

Теперь, когда у вас есть Future[JsResult], вы можете сопоставить неудавшиеся сценарии с желаемым результатом, а сценарий успеха - просто JsResult. Надеюсь, вы используете это в асинхронной структуре, которая ожидает, что вы будете кормить ее в будущем и у нее есть свое неудачное будущее для сопоставления ответов об ошибках (например, Play!).

+0

У вас все получилось, я использую Play! : D –

+0

Есть ли способ сделать это, не расширяя «Исключение»? –

+1

Не более чисто. Неудачное состояние Будущего принимает Throwable. И если вы не подведете будущее, вы не сможете коротко замыкаться в будущем для понимания. Любая конкретная причина избежать исключения, кроме этого, заставляет вас думать об уродливых потоках try/catch Java? Кроме того, исключения - это способ воспроизведения несоответствия запроса запроса, поэтому это идиоматический способ сделать это. –