Pull to refresh

Comments 13

Почему Expected?
Это ведь классический Either. Зачем вводить какие-то свои понятия?
Я вот тоже не понял. Ведь по сути, вавровский Try — это и так Either, содержащий либо результат, либо исключение (не параметризованное, т.е. Exception). И его можно к Either преобразовать, для этого есть метод toEither. А если хочется параметризованный — то Either к нашим услугам.
Можно применять Either. Названия и прототип для Expected<T, E> я взял с класса std::experemental::expected<T, E>, который планируется добавить в С++23 стандарт.
Например, в языке Scala для этого используется определенный класс Try.

На мой взгляд Try не самый удобный инструмент для обработки ошибок в Scala, см. например github.com/scala/bug/issues/6284. Лично мне больше по душе тайпкласс MonadError, где ошибка параметризована.
Это же вроде проблема конкретной реализации в скале, а не функциональной обработки ошибок как таковой?
Все верно, это by design. Я имел ввиду, что если у нас есть чистая функция (взята с потолка):
  def doSth[F[_]: MonadError[?[_], Throwable]](): F[Int] = for {
    i <- Applicative[F].pure(10)
  } yield i + 1


то использовать Try в качестве F не получится, т.к. Try нарушает правила функтора. А вот Either очень даже подойдет:

  doSth[Try]() // не компилируется, нет нужного инстанса, т.к. нарушено правило
  doSth[Either[Throwable, ?]]() //Ok


Это делает использование Try в функциональном как минимум проблематичным.
>Но увы, в данный момент, чтобы работать с исключения в функциональном стиле можно использовать библиотеку Vavr или писать свои классы на подобии к Expected.
Почему увы-то?
def inputStreamForURL(url: String): Try[Try[Try[InputStream]]]

В скале нет flat Map? :)
def inputStreamForURL(url: String): Try[Try[Try[InputStream]]] = parseURL(url).map { u =>
     Try(u.openConnection()).map(conn => Try(conn.getInputStream))
}

Try[Try[Try просто ужасен, можно ведь просто написать:


def inputStreamForURL(url: String): Try[InputStream] = Try { 
  parseURL(url).map(_.openConnection()).map(_.getInputStream())
}

У этих методов совершенно разная семантика. Throw это не просто результат функции, а именно раннее завершение всей цепочки вызовов до соответствующего catch-блока. Expected/Try — это урезанная семантика checked exception, в которой нельзя например выкидывать эксепшны разного типа (throws E1, E2). Так делается, потому что во многих функциональных языках нет другой альтернативы. В Java функциональщина всегда будет являться инородным телом и добавит бойлерплейта.

Для упрощения работы с подобными типами можно использовать pattern matching или методы visit.

matches(data,
        Expected::error,  e -> out.println("get error: " + e),
	Expected::value, v -> out.println("get value: " + v)
);
  1. Причём здесь Kotlin?
  2. Чего в результате вы хотели добиться по сравнению с обычными try-catch? (которые в Kotlin, кстати, являются выражениями)
Функциональную обработку ошибку можна применить как в Java так и в Kotlin.
В Котлине можно сделать еще симпатичнее визитор (применив перегрузку оператора вызова фукнции).

matches(data) (
        Expected::error,  e -> out.println("get error: " + e),
	Expected::value, v -> out.println("get value: " + v)
);

Sign up to leave a comment.

Articles