Scala Либо карта вправо, либо возврат налево
Можно ли справиться Either
аналогично Option
? В Option
, У меня есть getOrElse
функция, в Either
я хочу вернуться Left
или процесс Right
, Я ищу самый быстрый способ сделать это без какого-либо шаблона, как:
val myEither:Either[String, Object] = Right(new Object())
myEither match {
case Left(leftValue) => value
case Right(righValue) =>
"Success"
}
4 ответа
В Scala 2.12
Любой из них смещен вправо, что означает, что Право считается делом по умолчанию для работы. Если это Left, такие операции, как map, flatMap, ... возвращают значение Left без изменений
так что вы можете сделать
myEither.map(_ => "Success").merge
если вы найдете его более читабельным, чем fold
,
И Чантеп, и Март - хорошие решения вашей непосредственной проблемы. Но в более широком смысле трудно рассматривать Либо как нечто полностью аналогичное Option
в частности, позволяя вам выражать последовательности потенциально неудачных вычислений для понимания. Либо есть API проекции (используется в решении cchantep), но он немного сломан. (Либо проекции нарушают понимание с помощью охранников, сопоставления с образцом или назначения переменных).
FWIW, я написал библиотеку для решения этой проблемы. Это увеличивает либо с этим API. Вы определяете "предвзятость" для ваших Либо. "Правильное смещение" означает, что обычный поток (map, get и т. Д.) Представлен Right
объект пока Left
объекты представляют собой какую-то проблему. (Правое смещение является обычным, хотя вы также можете определить левое смещение, если вы предпочитаете.) Затем вы можете лечить Either
как Option
; он предлагает полностью аналогичный API.
import com.mchange.leftright.BiasedEither
import BiasedEither.RightBias._
val myEither:Either[String, Object] = ...
val o = myEither.getOrElse( "Substitute" )
Более полезно, теперь вы можете рассматривать Either как настоящую монаду scala, т.е. использовать flatMap, map, filter и для понимания:
val myEither : Either[String, Point] = ???
val nextEither = myEither.map( _.x ) // Either[String,Int]
или же
val myEither : Either[String, Point] = ???
def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ???
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint( p )
} yield {
(p, g.population)
}
}
Если все этапы обработки выполнены успешно, locPopPair
будет Right[Long]
, Если что-то пошло не так, это будет первым Left[String]
встречается.
Это немного сложнее, но хорошая идея определить пустой токен. Давайте посмотрим на небольшое изменение для понимания выше:
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint( p ) if p.x > 1000
} yield {
(p, g.population)
}
}
Что будет, если тест p.x > 1000
не удалось? Мы хотели бы вернуть некоторые Left
что означает "пустой", но не существует универсального подходящего значения (не все Left
это Left[String]
, На данный момент, что произойдет, код будет бросать NoSuchElementException
, Но мы можем сами указать пустой токен, как показано ниже:
import com.mchange.leftright.BiasedEither
val RightBias = BiasedEither.RightBias.withEmptyToken[String]("EMPTY")
import RightBias._
val myEither : Either[String, Point] = ???
def findGalaxyAtPoint( p : Point ) : Either[String,Galaxy] = ???
val locPopPair : Either[String, (Point, Long)] = {
for {
p <- myEither
g <- findGalaxyAtPoint( p ) if p.x > 1000
} yield {
(p, g.population)
}
}
Теперь, если p.x > 1000
тест не пройден, исключений не будет, locPopPair
просто будет Left("EMPTY")
,
Ты можешь использовать .fold
:
scala> val r: Either[Int, String] = Right("hello")
r: Either[Int,String] = Right(hello)
scala> r.fold(_ => "got a left", _ => "Success")
res7: String = Success
scala> val l: Either[Int, String] = Left(1)
l: Either[Int,String] = Left(1)
scala> l.fold(_ => "got a left", _ => "Success")
res8: String = got a left
Редактировать:
Перечитывая ваш вопрос, мне неясно, хотите ли вы вернуть значение в Left
или другой (определенный в другом месте)
Если это первое, вы можете пройти identity
в .fold
однако это может изменить тип возвращаемого значения на Any
:
scala> r.fold(identity, _ => "Success")
res9: Any = Success
Я думаю, вы можете сделать следующее.
def foo(myEither: Either[String, Object]) =
myEither.right.map(rightValue => "Success")
В scala 2.13 вы можете использовать myEither.getOrElse
Right(12).getOrElse(17) // 12
Left(12).getOrElse(17) // 17