Scala: как понять метод FlatMap Try?

Метод FlatMap Успеха выполняется следующим образом:

  def flatMap[U](f: T => Try[U]): Try[U] =
    try f(value)
    catch {
      case NonFatal(e) => Failure(e)
    }

Я понимаю, что делает этот метод, это помогает нам избежать написания большого количества ловушек.

Но в каком смысле он похож на обычную flatMap?

Регулярная flatMap берет последовательность последовательностей и помещает все элементы в одну большую "плоскую" последовательность.

Но метод FlatMap Try не совсем сглаживает что-либо.

Итак, как понять метод FlatMap Try?

Ответ 1

Не вступая в монады, вместо того, чтобы думать об этом с точки зрения коллекций, вы можете думать об этом в терминах структур (где коллекция становится структурой со многими записями).

Теперь взглянем на подпись Try.flatmap (из вашего сообщения):

def flatMap[U](f: T => Try[U]): Try[U] функция f преобразует T в Try [U] в контексте Try [T].

В противоположность этому, представьте, что операция была "map", результатом будет:

def badMap[U](f: T => Try[U]): Try[Try[U]]

Как вы можете видеть, flatmap "сглаживает" результат в контексте Try [T] и создает Try[U] вместо вложенного Try[Try[U]].

Вы можете применить ту же концепцию "сглаживания вложенной структуры" к коллекциям, как вы упоминаете.

Ответ 2

Вы можете считать Try [T] похожим на коллекцию только одного элемента (например, Option [T]).

Когда "последовательность последовательностей" является "только одной последовательностью", карта и плоская карта почти одинаковы. Единственная разница - это подпись функции.

В этом случае не требуется сплющивание.

Ответ 3

Я нашел Дэна Спивака " Монады не метафоры" очень полезно в моей голове вокруг монадов. Для людей, начинающих с Scala (как и я), это намного легче понять, чем что-либо еще, что я нашел - в том числе Odersky. Прочитав это, обратите внимание, что 'bind' == 'flatMap'.

Ответ 4

Как вы можете прочитать в Прогулка по Scala: Понятия последовательности: "В scala каждый тип данных, который поддерживает фильтр операций, map и flatMap (с соответствующими типами), может использоваться при последовательных вычислениях". На самом деле это означает, что вы можете угрожать ему, как монаде.
И flatMap для монады имеют такую ​​подпись:

def flatMap(f: A => M[B]): M[B]

Все коллекции в scala имеют монадические интерфейсы, поэтому вы можете смотреть на монадические операции в этой узкой области действия как операции над последовательностями. Но это еще не все. В случае, когда некоторые монады, глядя на них, как на коллекции, более запутанны, чем полезны. Как правило, flatMap применяет преобразование "содержания" монады, составляя эту монаду с помощью операции, приводящей к другому экземпляру монады того же типа. Таким образом, вы можете смотреть на монады по крайней мере двумя способами:

  • Монада - это своего рода коллекция (или поле, где-то что-то), а элементы этой коллекции - "контент".
  • Монада - это какой-то контекст, а элементы монады - это просто некоторые вычисления, сделанные в этом контексте.

Иногда легче думать о монаде как коллекции, иногда легче думать об этом как о контексте. По крайней мере для меня. На самом деле оба подхода взаимозаменяемы, т.е. Вы можете просматривать списки (коллекции) как недетерминированные вычисления, которые могут возвращать произвольное количество результатов.

Итак, в случае Try это может быть проще думать об этом как контексте исполнения, с двумя состояниями: Успех и Неудача. Если вы хотите составить несколько Tries, а затем один из них находится в состоянии Failure, тогда весь контекст станет Failure (цепочка сломана). В противном случае вы можете выполнять некоторые операции над "контентом" этого Tries, а контекст - "Успех".

Ответ 5

Регулярная flatMap берет последовательность последовательностей и помещает все элементы в одну большую "плоскую" последовательность.

Небольшая коррекция:

Регулярный flatMap принимает последовательность (в общем случае монаду), имеет аргумент, который является функцией, преобразующей элемент в последовательность (monad), и возвращает "плоскую" последовательность (monad).

В целях сравнения упомянутые здесь подстановки уровня gory:). Метод flatMap выполняет итерацию по входной последовательности, вызывающей f(element), но создает единственную новую последовательность результатов. Часть "сплющивания" применяется после каждого приложения аргументов функции, f(element) - она ​​выполняет вложенную итерацию по полученной подпоследовательности, давая каждую запись в единственной последовательности результатов.

Эквивалент для Success, с value внутри (чаще всего монада):

  • flatMap имеет аргумент, который является функцией преобразования Success в Try= Success(value) OR Failure(exception). После применения f(value) результат уже равен Try. "Сплющенная" часть является тривиальной/нулевой операцией: итерация по этому результату функции даст только одну запись, поэтому Try/Success/Failure даже не нужно реализовывать Iterable). Не обертывает дополнительные слои Success/Failure, и поэтому возвращает "плоский" Try.

    т.е. "Плоская" часть означает, что она не каскадирует обертки Success/Failure, так как последовательность flatMap не выполняет каскадные последовательности в иерархии (дерева значений).

  • это отличается от map, аргументом которого является функция, преобразующая Success в произвольный тип U; после применения f(value) карта должна добавить дополнительный слой новой Success/Failure, обертывающей вокруг value/exception.

Ответ 6

Регулярная flatMap принимает последовательность последовательностей и помещает все элементы в одну большую "плоскую" последовательность

Было бы справедливо заменить последовательность слов на монаду здесь, потому что эта операция не относится только к коллекции, на самом деле коллекции также являются монадами. Подумайте о Try как коллекции, которая может содержать значение Success Failure