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>)
(... и обязательно преобразуйте бесточечную функцию, если поначалу это не имеет смысла.)


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