Can I use different workflows simultaneously in F#?

前端 未结 3 1686
感动是毒
感动是毒 2021-01-25 11:19

I need my state to be passed along while being able to chain functions with the maybe workflow. Is there a way for 2 workflows to share the same context? If no, what is the way

相关标签:
3条回答
  • 2021-01-25 11:26

    As stated in the previous answer, one way to combine workflows in F# (Monads in Haskell) is by using a technique called Monad Transformers.

    In F# this is really tricky, here is a project that deals with that technique.

    It's possible to write the example of the previous answer by automatically combining State and Maybe (option), using that library:

    #r @"c:\packages\FSharpPlus-1.0.0\lib\net45\FSharpPlus.dll"
    
    open FSharpPlus
    open FSharpPlus.Data
    
    // Stateful computation
    let computation =
        monad {
            let! x = get
            let! y = OptionT (result (Some 10))
            do! put (x + y)
            let! x = get
            return x
        }
    
    printfn "Result: %A" (State.eval (OptionT.run computation) 1)
    

    So this is the other alternative, instead of creating your custom workflow, use a generic workflow that will be automatically inferred (a-la Haskell).

    0 讨论(0)
  • 2021-01-25 11:42

    Others already gave you a direct answer to your question. However, I think that the way the question is stated leads to a solution that is not very idiomatic from the F# perspective - this might work for you as long as you are the only person working on the code, but I would recommend against doing that.

    Even with the added details, the question is still fairly general, but here are two suggestions:

    • There is nothing wrong with reasonably used mutable state in F#. For example, it is perfectly fine to create a function that generates IDs and pass it along:

      let createGenerator() = 
        let currentID = ref 0
        (fun () -> incr currentID; !currentID)
      
    • Do you really need to generate the IDs while you are building the entities? It sounds like you could just generate a list of entities without ID and then use Seq.zip to zip the final list of entities with list of IDs.

    • As for the maybe computation, are you using it to handle regular, valid states, or to handle exceptional states? (It sounds like the first, which is the right way of doing things - but if you need to handle truly exceptional states, then you might want to use ordinary .NET exceptions).

    0 讨论(0)
  • 2021-01-25 11:47

    In F# you cannot easily mix different types of computation expressions as you would do in Haskell by using Monad Transformers or similar techniques. You could however build your own Monad, embedding state threading and optional values, as in:

    type StateMaybe<'T> = 
        MyState -> option<'T> * MyState
    
    // Runs the computation given an initial value and ignores the state result.
    let evalState (sm: StateMaybe<'T>) = sm >> fst
    
    // Computation expression for SateMaybe monad.
    type StateMaybeBuilder() =
        member this.Return<'T> (x: 'T) : StateMaybe<'T> = fun s -> (Some x, s)
        member this.Bind(sm: StateMaybe<'T>, f: 'T -> StateMaybe<'S>) = fun s ->
            let mx,s' = sm s
            match mx with
            | Some x    -> f x s'
            | None      -> (None, s)
    
    // Computation expression builder.
    let maybeState = new StateMaybeBuilder()
    
    // Lifts an optional value to a StateMaybe.
    let liftOption<'T> (x: Option<'T>) : StateMaybe<'T> = fun s -> (x,s)
    
    // Gets the current state.
    let get : StateMaybe<MyState> = fun s -> (Some s,s)
    
    // Puts a new state.
    let put (x: MyState) : StateMaybe<unit> = fun _ -> (Some (), x)
    

    Here's an example computation:

    // Stateful computation
    let computation =
        maybeState {
            let! x = get
            let! y = liftOption (Some 10)
            do! put (x + y)
            let! x = get
            return x
        }
    
    printfn "Result: %A" (evalState computation 1)
    

    StateMaybe may be generalized further by making the type of the state component generic.

    0 讨论(0)
提交回复
热议问题