правильно используя правую сторону непересекающегося союза - PullRequest
0 голосов
/ 01 ноября 2019

каков лучший способ превратить правый [список] в список

Я проанализирую строку Json следующим образом:

val parsed_states = io.circe.parser.decode[List[List[String]]](source)

И это создаст значение, эквивалентное этому

val example_data = Right(List(List("NAME", "state"), List("Alabama", "01"), List("Alaska", "02"), List("Arizona", "04")))

Я пытаюсь получить Right, Left, Either и реализовать лучший способ получить список пар StateName и StateValue из этого списка выше.

Я вижу, что любой из этих способов даст мне то, что мне нужно (при удалении заголовка):

val parsed_states = example_data.toSeq(0).tail
val parsed_states = example_data.getOrElse(<ProbUseNoneHere>).iterator.to(Seq).tail
val parsed_states = example_data.getOrElse(<ProbUseNoneHere>).asInstanceOf[Seq[List[String]]].tail

Я думаю, мне интересно, должен ли я сделать это так или иначе, основываясь на возможном поведениивверх по течению, выходящий из io.circe.parser.decode или я обдумываю этоЯ новичок в парадигме Right, Left, Either и не нахожу ужасно много полезных примеров.

в ответ на @ slouc

, пытающийся соединить точки из вашегоответьте, как они относятся к этому варианту использования. ну как то так?

    def blackBox: String => Either[Exception, List[List[String]]] = (url:String) => {
      if (url == "passalong") {
        Right(List(List("NAME", "state"), List("Alabama", "01"), List("Alaska", "02"), List("Arizona", "04")))
      }
      else Left(new Exception(s"This didn't work bc blackbox didn't parse ${url}"))
    }
    //val seed = "passalong"
    val seed = "notgonnawork"
    val xx: Either[Exception, List[List[String]]] = blackBox(seed)
    def ff(i: List[List[String]]) = i.tail
    val yy = xx.map(ff)
    val zz = xx.fold(
      _  => throw new Exception("<need info here>"),
      i => i.tail)

1 Ответ

2 голосов
/ 01 ноября 2019

Хитрость в том, что не получает пары «имя состояния / значение состояния» из Either. Они должны храниться внутри. Если вы хотите, вы можете преобразовать тип Either во что-то другое (например, Option, отбросив все, что у вас было слева), но не уничтожайте эффект. Что-то должно быть там, чтобы показать, что декодирование могло быть неудачным;это может быть Either, Option, Try и т. д. В конце концов вы соответственно обработаете левый и правый регистр, но это должно произойти как можно позже.

Давайте рассмотрим следующий тривиальный пример:

val x: Either[String, Int] = Right(42)
def f(i: Int) = i + 1

Вы можете утверждать, что вам нужно получить 42 из Right, чтобы вы могли передать его в f. Но это не правильно. Давайте перепишем пример:

val x: Either[String, Int] = someFunction()

Что теперь? Мы не знаем, есть ли у нас Left или Right в значении x, поэтому мы не можем «вытащить это». Какое целое число вы бы получили, если это Left? (если у вас действительно есть целочисленное значение для использования в этом случае, это вполне справедливо, и я рассмотрю этот вариант использования чуть позже)

Вместо этого вам нужно будет сохранить эффект (в данном случае Either), и вам необходимо продолжить работу в контексте этого эффекта. Это должно показать, что в вашей программе был пункт (в данном случае someFunction(), или расшифровка в вашем исходном вопросе), который мог пойти не так.

Так что если вы хотите применить f к вашемупотенциальное целое число, вам нужно map эффект с ним (мы можем сделать это, потому что Either является функтором, но это деталь, которая, вероятно, выходит за рамки этого ответа):

val x: Either[String, Int] = Right(42)
def f(i: Int) = i + 1

val y = x.map(value => f(value)) // Right(43)
val y = x.map(f) // shorter, point-free notation

и

val x: Either[String, Int] = someFunction()
def f(i: Int) = i + 1

// either a Left with some String, or a Right with some integer increased by 1
val y = x.map(f) 

Затем, в самом конце цепочки вычислений, вы можете обрабатывать случаи Left и Right;например, если вы обрабатывали HTTP-запрос, то в случае Left вы могли бы вернуть 500, а в случае Right вернуть 200.

Для обращения к сценарию использования с указанным значением по умолчаниюраньше - если вы действительно хотите это сделать, избавиться от Left и в этом случае разрешить какое-то значение (например, 0), тогда вы можете использовать fold:

def f(i: Int) = i + 1

// if x = Left, then z = 0
// if x = Right, then z = x + 1
val z = x.fold(_ => 0, i => i + 1)
...