2014-05-22 1 views
2

Я рассматривал множество примеров трансформаторов монахов Scala и не мог понять, как делать то, что, по моему мнению, может быть чем-то простым. Я хочу написать понимание for, которое ищет что-то в базе данных (MongoDB), которая возвращает Option, тогда если Option является Some, он просматривает его содержимое и получает еще Option и так далее. На каждом шаге, если я получаю None, я хочу прервать все это и создать сообщение об ошибке, например "X not found". Понятие for должно содержать Either (или что-то подобное), в котором Left содержит сообщение об ошибке, а Right содержит успешный результат всей операции (возможно, только строку или объект, построенный с использованием нескольких значений, полученных вдоль путь).Monad transformer в Scala для понимания обрабатывать Опция и собирать сообщения об ошибках

До сих пор я просто использую Option монады само по себе, так как в этом тривиальном примере:

val docContentOpt = for { 
    doc <- mongoCollection.findOne(MongoDBObject("_id" -> id)) 
    content <- doc.getAs[String]("content") 
} yield content 

Однако, я застрял, пытаясь интегрировать что-то вроде Either в этом. Я ищу фрагмент рабочего кода, а не просто предложение попробовать \/ в Scalaz. Я попытался понять Скалаз, но у него очень мало документации, и мало что существует, кажется, написано для людей, которые знают все об исчислении лямбда, чего у меня нет.

+2

Не знаю, если вы но Scala 2.10 имеет Try the monad для обработки успеха и неудачи: http://www.scala-lang.org/api/current/#scala.util.Try – reggoodwin

+1

Поскольку вы имеете дело с той же монадой (Option) Трансформаторы Monad - это не то, что вы ищете, трансформаторы Monad должны составлять разные типы монад (например, List [Option], Future [Try] и т. Д.) – GClaramunt

+0

@GClaramunt Ah, OK. Я предположил, что мне нужна монада Option, плюс одна монада, отсюда и потребность в монад-трансформаторе. Спасибо, что разъяснил это. –

ответ

2

Я «попробовать» что-то вроде этого:

def tryOption[T](option: Option[T], message:String =""):Try[T] = option match { 
    case Some(v) => Success(v) 
    case None => Failure(new Exception(message)) 
} 

val docContentOpt = for { 
    doc <- tryOption(mongoCollection.findOne(MongoDBObject("_id" -> id)),s"$id not found") 
    content <- tryOption(doc.getAs[String]("content"), "content not found") 
} yield content 

Основном возможность попробовать преобразования, который фиксирует ошибку в виде исключения. Try - это специализированное правое смещение. Либо монодальное (в отличие от Либо, а это не так)

+0

Вы можете заменить 'tryOption' методом' fold' 'Option', как описано, например.здесь: http://stackoverflow.com/a/17526264/1805388 – ValarDohaeris

+0

@ ValarDohaeris Я нахожу катаморфизм по опции очень нечитабельно, поскольку они всегда начинаются с отказа: «Виноват, пока не окажется невиновным». Но это действительно более красноречиво. – maasg

+0

@maasg Спасибо, это отлично выглядит. –

2

Try может быть тем, что вы ищете, но это также возможно сделать, используя «правую проекцию» стандартная библиотека Either:

val docContentOpt: Either[String, String] = for { 
    doc <- mongoCollection.findOne(MongoDBObject("_id" -> id)).toRight(
    s"$id not found" 
).right 
    content <- doc.getAs[String]("content").toRight("Can't get as content").right 
} yield content 

Это может иметь больше смысла, если ваш тип ошибки не распространяется Throwable, например, или, если вы застряли на 2.9.2 или выше (или если вы просто предпочитаете общность Either, и т.п.).

(В качестве примечания, было бы неплохо, если стандартная библиотека предоставляется toSuccess и toFailure методы на Option, что бы сделать преобразование Option в Try так удобно, как преобразование Option в Either здесь, может быть, когда-нибудь.)

(И как еще одна заметка, Scalaz на самом деле не покупает вас здесь, это позволит вам написать .toRightDisjunction("error") вместо .toRight("error").right, но об этом. Как отмечает Габриэль Кларамунт в комментарии, это не случай для monad transformers.)

Смежные вопросы