Как это:
for {
tuple <- getConfigs()
} println(tuple)
Шутки в сторону, я думаю, что это интересный вопрос, но его немного называют неправильно.
Проблема (см. Выше) не в том, что для понимания невозможно, а в том, что сопоставление с образцом внутри для понимания невозможно в Either
.
Существует документация о том, как переводы понимаются , но они не охватывают каждый случай. Насколько я вижу, этот там не освещен. Так что я посмотрел это в моем экземпляре «Программирование в Scala» - второе издание (потому что это то, что у меня есть на мертвых деревьях).
Раздел 23.4 - Перевод выражений
Существует подраздел «Перевод шаблонов в генераторах», в чем и заключается проблема, как описано выше. В нем перечислены два случая:
Случай первый: кортежи
Это точно наш случай:
for ((x1, …, xn) <- expr1) yield expr2
следует перевести на expr1.map { case (x1, …, xn) => expr2)
.
Это именно то, что делает IntelliJ, когда вы выбираете код и выполняете действие «Desugar для понимания». Ура!
… Но это делает его еще более странным в моих глазах, потому что код desugared на самом деле запускает без проблем.
Таким образом, этот случай (imho) соответствует случаю, но не , что происходит. По крайней мере, не то, что мы наблюдали. Хм?!
Случай два: Произвольные схемы
for (pat <- expr1) yield expr2
переводится как
expr1 withFilter {
case pat => true
case _ => false
} map {
case pat => expr2
}
там, где сейчас есть withFilter
метод!
Этот случай полностью объясняет сообщение об ошибке и почему сопоставление с шаблоном в Either
невозможно.
Глава в конечном счете ссылается на спецификацию языка scala (хотя и на более старую), где я остановлюсь сейчас.
Так что извините, я не могу полностью ответить на этот вопрос, но, надеюсь, я мог бы достаточно намекнуть, в чем здесь корень проблемы.
Интуиция
Так почему же Either
проблематично и не предлагает метод withFilter
, где Try
и Option
делают?
Поскольку filter
удаляет элементы из «контейнера» и, возможно, «все», поэтому нам нужно что-то, представляющее «пустой контейнер».
Это легко для Option
, где это, очевидно, None
. Также легко, например, List
. Не так просто для Try
, потому что есть несколько Failure
, каждый из которых может содержать определенное исключение. Однако есть несколько сбоев в этом месте:
NoSuchElementException
и
UnsupportedOperationException
и именно поэтому Try[X]
работает, а Either[Throwable, X]
- нет.
Это почти то же самое, но не совсем. Try
знает, что Left
- это Throwable
, и авторы библиотеки могут воспользоваться этим.
Однако для Either
(который теперь смещен вправо) «пустым» является случай Left
; который является общим. Таким образом, пользователь определяет, какой это тип, поэтому авторы библиотеки не могли выбрать общие экземпляры для каждого возможного оставленного.
Я думаю, именно поэтому Either
не предоставляет withFilter
из коробки и почему ваше выражение не получается.
Btw. Символ
expr1.map { case (x1, …, xn) => expr2) }
кейс работает, потому что он выбрасывает MatchError
в стек вызовов и паникует из проблемы, которая… сама по себе может быть более серьезной проблемой.
Да, и для тех, кто достаточно смел: до сих пор я не использовал слово "Монада", потому что у Скалы нет структуры данных для него, но для понимания достаточно просто. Но, возможно, ссылка не помешает: Аддитивные монады имеют это "нулевое" значение, которое именно то, что Either
пропускает здесь, и то, что я пытался дать некоторое значение в части "интуиция".