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 ответа

Решение

И Чантеп, и Март - хорошие решения вашей непосредственной проблемы. Но в более широком смысле трудно рассматривать Либо как нечто полностью аналогичное 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

Другие вопросы по тегам