I would call it a monadic stack.
You can define your own operators, but if you want to map them, you need something like a "deep map level 2" which normally is (map >> map) so, in this case:
Some (Result<int,string>.Ok 1) |> (Result.map >> Option.map) ((+) 1)
// val it : Result<int,string> option = Some (Ok 2)
If you want to explore more F#+ provides limited support for this through 2 different abstractions:
- Composed functors: by using the
Compose type from FSharpPlus.Data:
let v1 = Compose (Some (Result<int,string>.Ok 1))
let v2 = v1 |> map ((+) 1)
// .. more operations, then when you're finished ..
let v3 = Compose.run v2
// val v1 : Compose<Result<int,string> option> = Compose (Some (Ok 1))
// val v2 : Compose<Result<int,string> option> = Compose (Some (Ok 2))
// val v3 : Result<int,string> option = Some (Ok 2)
So it allows you to compose arbitrary Functors, which are types you can map on.
- Monad transformers: by using in this particular case the
ResultT type from FSharpPlus.Data:
The previous example would work, just replace Compose for ResultT, but now you can also do monadic operations:
let x = monad' {
let! v1 = ResultT (Some (Result<int,string>.Ok 1))
let! v2 = ResultT (Some (Result<int,string>.Ok 1))
return v1 + v2 }
ResultT.run x
// val x : ResultT<Result<int,string> option> = ResultT (Some (Ok 2))
// val it : Result<int,string> option = Some (Ok 2)