left
и right
являются важными. Either
полезен без проекций (в основном вы делаете сопоставление с образцом), но проекции заслуживают внимания, так как они дают гораздо более богатый API. Вы будете использовать соединения гораздо меньше.
Either
часто используется для обозначения «правильного значения или ошибки». В этом отношении он похож на расширенный Option
. Когда нет данных, вместо None
появляется ошибка.
Option
имеет богатый API. То же самое можно сделать доступным на Either
, при условии, что мы знаем в любом из них, какой из них является результатом, а какой - ошибкой.
left
и right
проекция говорит именно об этом. Это Either
, плюс добавленное знание, что значение находится соответственно слева или справа, а другое является ошибкой.
Например, в Option
вы можете отобразить, поэтому opt.map(f)
возвращает Option
с f
, примененным к значению opt
, если оно есть, и все еще None
, если opt
было None
. В левой проекции он применяет f
к значению слева, если оно равно Left
, и оставляет его неизменным, если оно равно Right
. Соблюдайте подписи:
- In
LeftProjection[A,B]
, map[C](f: A => C): Either[C,B]
- В
RightProjection[A,B]
, map[C](f: B => C): Either[A,C]
.
left
и right
- просто способ сказать, какая сторона считается значением, если вы хотите использовать одну из обычных процедур API.
Альтернативы могли быть:
- устанавливает соглашение, как в Haskell, где были веские синтаксические причины для правильного определения значения. Если вы хотите применить метод на другой стороне (вы, возможно, захотите изменить ошибку, например, на
map
), выполните swap
до и после.
- Имена методов постфикса с левым или правым (возможно, только L и R). Это помешало бы использовать для понимания. С
for
пониманиями (на самом деле flatMap
, но для обозначения вполне удобно) Either
является альтернативой (проверенным) исключениям.
Теперь присоединяется. Левый и правый означает то же самое, что и для проекций, и они тесно связаны с flatMap
. Рассмотрим joinLeft
. Подпись может быть загадочной:
joinLeft [A1 >: A, B1 >: B, C] (implicit ev: <:<[A1, Either[C, B1]]):
Either[C, B1]
A1
и B1
технически необходимы, но не критичны для понимания, давайте упростим
joinLeft[C](implicit ev: <:<[A, Either[C, B])
Что подразумевается, это то, что метод может быть вызван, только если A
является Either[C,B]
. Метод недоступен для Either[A,B]
в целом, но только для Either[Either[C,B], B]
. Как и в случае левой проекции, мы считаем, что значение слева (это было бы правильно для joinRight
). То, что делает соединение, сглаживает это (подумайте flatMap
) Когда один присоединяется, его не волнует, является ли ошибка (B) внутри или снаружи, мы просто хотим либо [C, B]. Таким образом, Left (Left (c)) уступает Left (c), а Left (Right (b)) и Right (b) дают Right (b). Связь с flatMap следующая:
joinLeft(e) = e.left.flatMap(identity)
e.left.flatMap(f) = e.left.map(f).joinLeft
Эквивалент Option
будет работать на Option[Option[A]]
, Some(Some(x))
даст Some(x)
, а Some(None)
и None
- None
. Это может быть написано o.flatMap (личность). Обратите внимание, что Option[A]
изоморфно Either[A,Unit]
(если вы используете левые проекции и объединения), а также Either[Unit, A]
(используя правые проекции).