I'm learning monadic composition through Scott Wlaschin's Railway-oriented Programming post. Oncebind
, switch
, and >=>
functions are defined, he introduces map
to show how to "turn a one-track function into a two-track function". That is:
f: a -> b => f': T<a,c> -> T<b,c>
The implementation in the article is the following:
let map oneTrackFunction twoTrackInput =
match twoTrackInput with
| Success s -> Success (oneTrackFunction s)
| Failure f -> Failure f
Did an an equivalent implementation using switch
and bind
as an exercise,
let map' f = bind (switch f)
but when I tried to implement map
with >=>
, I arrived at this ugly mess:
let map'' f result =
match result with
| Ok o -> ((fun _ -> result) >=> (switch f)) o
| Error e -> Error e
Note to self: o
could be any value of type 'a
(if result : Result<'a,'c>
), because f
's input is already saved in the closure used as >=>
's first operand, but this was the only way I could think of to keep it more generic.
Is there a "cleaner" implementation similar to map'
s?
I used the following example to test the map
s above:
map ((+) 2) ((Ok 27) : Result<int,string>)
Used implementations of bind
, switch
, and >=>
:
let bind
( f : 'a -> Result<'b,'c>)
(result : Result<'a,'c>)
=
match result with
| Ok o -> f o
| Error e -> Error e
let switch
(f : 'a -> 'b)
(x : 'a )
=
f x |> Ok
let (>=>)
(f : 'a -> Result<'b,'error>)
(g : 'b -> Result<'c,'error>)
=
f >> (bind g)
Referring to German wikipedia with examples in Haskell, there are three ways to define the set of monadic operators: either with
Since you have the Kleisli operator already and want to derive map
from it, you only need return
(which is Ok
here) for this:
let fmap f = id >=> (Ok << f)
// val fmap : f:('a -> 'b) -> (Result<'a,'c> -> Result<'b,'c>)
F# code for the three alternatives:
module Result1 =
// Definition by fmap and join
let fmap = Result.map
let join mma = Result.bind id mma
// derived:
let (>>=) ma f = (join << fmap f) ma
let (>=>) f g = join << fmap g << f
module Result2 =
// Definition by return and >>=
let ``return`` = Ok
let (>>=) ma f = Result.bind f ma
// derived:
let (>=>) f g a = f a >>= g
let fmap f ma = ma >>= (``return`` << f)
let join mma = mma >>= id
module Result3 =
// Definition by return and >=>
let ``return`` = Ok
let (>=>) f g a = Result.bind g (f a)
// derived:
let (>>=) ma f = (id >=> f) ma
let fmap f = id >=> (``return`` << f)
let join mma = (id >=> id) mma