I find myself returning type Option<Result<'a,'b>>
a lot, my reasoning is the following:
I use Option to decide if I'm interested in processing an event, and if I do, then I return a result of processing it.
Is there a proper name for this pattern? And better operators? For example, I use: Option.map(Result.map f)
is there an operator for mapping nested functors?
I think the existing answer from Gus answers your question about the name of the pattern. In Haskell, this is achieved using monad transformers and the F#+ library lets you do this in F# too.
However, this pattern is not very common in most F# code. One reason is that it leads to very complicated code (both the types get complex and the processing code gets pretty cumbersome).
If you find yourself using Option<Result<'T>>
often, then I would consider if it makes sense to define a new type for this, perhaps using names that makes sense in your domain, so that the reader of the code can easily understand it. You could use something like:
type ProcessingResult<'T> =
| Ignored
| Failed of string
| Accepted of 'T
You will lose built-in functions for Option
and Result
, but you can implement the ones you need yourself. It is not too complicated:
module Processing =
let map f = function
| Ignored -> Ignored
| Failed s -> Failed s
| Accepted v -> Accepted (f v)
The benefit of this is that Processing.map
is much easier to read, which I think is a trade-off worth making.
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:
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.
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)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With