Почему `let fmap f = id >=> (Ok << f)` работает?

На вопрос Как реализовать карту с использованием оператора рыбы (>=>, композиция Клейсли) в F#? пару часов назад, и ответ Кефера поразил меня:

let fmap f = id >=> (Ok << f)

Он идеален в своей простоте, но когда я смотрю на сигнатуры типов, он не должен работать. Я занимаюсь этим уже почти час, поэтому, вероятно, упускаю что-то фундаментальное, как F# оценивает выражения...

Для справки, это реализации bind, switch и >=>:

let bind
    (     f : 'a -> Result<'b,'c>)
    (result :       Result<'a,'c>)
    =
    match result with 
    |    Ok o -> f o
    | Error e -> Error e

let (>=>)
    (f : 'a -> Result<'b,'error>)
    (g : 'b -> Result<'c,'error>)
    =
    f >> (bind g)

Моя следующая попытка заключалась в использовании альтернативной реализации для >=>:

let (>=>>)
    (f : 'a -> Result<'b,'error>)
    (g : 'b -> Result<'c,'error>)
    x
    =
    match (f x) with
    |    Ok o -> g o
    | Error e -> Error e

Вот тестовый вызов dotnet fsi:

(id >=>> (Ok << ((+) 2) : int -> Result<int,string>))
((Ok 27) : Result<int,string>)
//=> Ok 29

Я уже застрял в вопросе, почему >=>> не взрывается id в качестве первого аргумента?

Я думаю, что вот так происходит оценка, но, видимо, это не так:

id >=>> switch ((+) 2)
          |
          V
(>=>>) id (switch ((+) 2))
          |
          V
    match (id x) with
    |    Ok o -> (Ok << ((+) 2)) o
    | Error e -> Error e

На заметку будущему себе:

(>=>>)                           (>=>>)
  (f : 'a -> Result<'b,'error>)    id
  (g : 'b -> Result<'c,'error>)    (Ok << ((+) 2) : int -> Result<int,string>))
  x                                ((Ok 27) : Result<int,string>)

(... и обязательно преобразуйте бесточечную функцию, если поначалу это не имеет смысла.)

Помимо понимания того, как это работает, что является прекрасным упражнением, этот тип кода определенно не рекомендуется использовать в F#. Посмотрите эту часть одного из выступлений Дона Сайма.

bmitc 03.04.2024 20:52
Введение в одну из самых важных концепций в React - функциональное программирование
Введение в одну из самых важных концепций в React - функциональное программирование
React разработан с использованием концепции функционального программирования, поэтому понимание функционального программирования важно для изучения...
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Фото ️🔁 Radek Jedynak 🔃 on ️🔁 Unsplash 🔃
Что такое Java 8 Streams API? Java 8 Stream API
3
1
94
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Поскольку в выражении let fmap f = id >=> (Ok << f) есть круглые скобки, нам нужно сначала вычислить внутреннее выражение. Это Ok << f часть.

Что означает Ok << f? Вы можете задать вопрос ФСИ:

> Ok;;
val it: ResultValue: 'a -> Result<'a,'b>

Итак, это функция, которая принимает 'a и возвращает Result<'a,'b>.

Хорошо, а что насчет Ok << f? Вы не можете просто спросить об этом FSI, потому что изолированно f не определено, но вы можете спросить FSI, как будет выглядеть лямбда-выражение, принимающее f:

> fun f -> Ok << f;;
val it: f: ('a -> 'b) -> ('a -> Result<'b,'c>)

Из этого мы узнаем, что FSI и компилятор F# делают вывод, что f должна быть функцией, которая принимает 'a и возвращает 'b. Это имеет смысл, поскольку это наиболее общая интерпретация выражения Ok << f: вы берете любую функцию f и объединяете ее выходные данные с функцией Ok, и вы получаете функцию 'a -> Result<'b,'c> в качестве возвращаемого значения.

Наконец, как насчет id >=> (Ok << f)?

Оператор Клесли >=> берет две функции, подобные «лифту», и объединяет их. Мы уже знаем, как выглядит выражение в правой части:

('a -> Result<'b,'c>)

Таким образом, в левой части должно быть какое-то выражение, возвращающее Result<'a,'c>: ?? -> Result<'a,'c>.

А поскольку id возвращает свой ввод ('T -> 'T) можно только предположить, что он имеет тип

Result<'a,'c> -> Result<'a,'c>

Итак, это все проверки типов. Это очень окольный способ реализации fmap, но он работает.

Другие вопросы по теме

Похожие вопросы

Пользовательский экземпляр `Read` завершается с ошибкой, когда тип обернут в Max, но производный экземпляр работает. Что не так с моим экземпляром Read?
В Google Sheets (разрешены только встроенные функции, без скрипта Google Apps). Можно ли имитировать функцию канала?
Есть ли функциональный способ сопоставить список (N элементов) со списком сумм соседних элементов (N-1 элементов) в Котлине?
Как реорганизовать цикл с помощью итератора. (Вернувшись из закрытия)
Настройка значений различных полей классов Java по одному значению для некоторого значения счетчика
Группировка по нескольким полям и подсчет использования в Java 8
Захват переменных: как переменные ведут себя при замыкании функций
Составление поставщиков React с реквизитами Value в Typescript
КакatomicModifyIORef может стать причиной утечек? И почемуatomicModifyIORef решает проблему?
Как я могу изменить плагин монитора Kbd XMobar так, чтобы нажатие на него повторялось по макетам?