Is there a standard monad for the “opposite,” possibly monads?

Perhaps the monad allows you to associate a set of operators, all of which can fail (returning None), and in the end returns Some result if each superoperation succeeds, or None if something failed. Here is a small example of a dummy example:

 type MaybeBuilder() = member this.Return(x) = Some x member this.Bind(m, f) = match m with | Some x -> fx | None -> None let maybe = MaybeBuilder() let list = [1;2;3;4] // evaluates to Some 3 maybe { let! x1 = List.tryFind ((=) 1) list let! x2 = List.tryFind ((=) 2) list return x1 + x2 } // evaluates to None maybe { let! x1 = List.tryFind ((=) 1) list let! x2 = List.tryFind ((=) 6) list return x1 + x2 } 

This is roughly equivalent to this:

 // evaluates to Some 3 match List.tryFind ((=) 1) list with | None -> None | Some x1 -> match List.tryFind ((=) 2) list with | None -> None | Some x2 -> Some (x1 + x2) // evaluates to None match List.tryFind ((=) 1) list with | None -> None | Some x1 -> match List.tryFind ((=) 6) list with | None -> None | Some x2 -> Some (x1 + x2) 

In a piece of code, I am now doing the “opposite” of this, returning the first successful hit:

 // evaluates to Some 1 match List.tryFind ((=) 1) list with | Some x1 -> Some x1 | None -> match List.tryFind ((=) 2) list with | Some x2 -> Some x2 | None -> None // evaluates to Some 2 match List.tryFind ((=) 6) list with | Some x1 -> Some x1 | None -> match List.tryFind ((=) 2) list with | Some x2 -> Some x2 | None -> None 

Is it also possible to do this with the monad to get the syntax of the syntax expression?

+6
source share
5 answers

Some time ago I wrote a blog that implements an imperative computing builder in F # . For example, the following calculation, which returns 0 and never executes the printfn :

 let test() = imperative { return 0 printfn "after return!" return 1 } 

I think your sample code can be written as:

 imperative { return! List.tryFind ((=) 1) list return! List.tryFind ((=) 2) list } 

How it works?

As you suppose (and Lee mentioned this), the type is also based on the option<'T> , with the slight difference that I use the value of the parameter with a delay (so you can make calculations without evaluating them) so the type of monadic type is actually :

 type Imperative<'T> = unit -> option<'T> 

The key trick in the calculation builder is to add Combine (which behaves like mplus in the version of Lee Haskell), which starts the first calculation and returns its result (if it was Some ) or starts the rest (if it was None ) (both a and b on the are functions here, so we need to call them and delay the result):

 member x.Combine(a, b) = (fun () -> match a() with | Some(v) -> Some(v) // if it returned, we can return the result immediately | _ -> b() ) // otherwise, we need to run the second part 

It really works very well - you can add support for loops and exception handling, and if you make the type more complex, you can add other functions like break :

 imperative { for x in 1 .. 5 do if (x % 2 = 0) then do! continue printfn "number = %d" x } 
+8
source

Thomas decision with

 imperative { return! List.tryFind ((=) 1) list return! List.tryFind ((=) 2) list } 

does what I wanted, but I only realized that I could also achieve what I needed just simply with this:

 // evaluates to Some 1 [1;2] |> List.tryPick (fun x -> List.tryFind ((=) x) list) // evaluates to Some 2 [6;2] |> List.tryPick (fun x -> List.tryFind ((=) x) list) 
+6
source

Haskell does this with a class of type MonadPlus , defined as:

 class Monad m => MonadPlus m where mzero :: ma mplus :: ma -> ma -> ma 

Maybe implements a class of this type as

 instance MonadPlus Maybe where mzero = Nothing Nothing `mplus` Nothing = Nothing Just x `mplus` Nothing = Just x Nothing `mplus` Just x = Just x Just x `mplus` Just y = Just x 

The mzero and mplus MonadPlus members MonadPlus match the Zero and Combine members used by the F # calculation expressions.

+5
source

You can also define a simple function for this:

 let orElse f = function | None -> f() | Some _ as x -> x 

You could combine as many functions as you want with the return of the first Some result as a result of the whole expression:

 List.tryFind ((=) 1) list |> orElse (fun () -> List.tryFind ((=) 2) list) |> orElse (fun () -> List.tryFind ((=) 3) list) |> orElse (fun () -> List.tryFind ((=) 4) list) 
+4
source

This particular case can also be emulated using the sequences:

 let tests = seq { yield List.tryFind ((=) 5) list yield List.tryFind ((=) 3) list yield List.tryFind ((=) 6) list } tests |> Seq.tryFind Option.isSome |> Option.bind id 
+1
source

Source: https://habr.com/ru/post/955992/


All Articles