How do I write a computation expression builder that accumulates a value and also allows standard language constructs?

前端 未结 4 1597
悲哀的现实
悲哀的现实 2021-02-03 10:34

I have a computation expression builder that builds up a value as you go, and has many custom operations. However, it does not allow for standard F# language constructs, and I\'

4条回答
  •  时光说笑
    2021-02-03 10:48

    The most complete examples I've seen are in §6.3.10 of the spec, especially this one:

    /// Computations that can cooperatively yield by returning a continuation
    type Eventually<'T> =
        | Done of 'T
        | NotYetDone of (unit -> Eventually<'T>)
    
    []
    module Eventually =
    
        /// The bind for the computations. Stitch 'k' on to the end of the computation.
        /// Note combinators like this are usually written in the reverse way,
        /// for example,
        ///     e |> bind k
        let rec bind k e =
            match e with
            | Done x -> NotYetDone (fun () -> k x)
            | NotYetDone work -> NotYetDone (fun () -> bind k (work()))
    
        /// The return for the computations.
        let result x = Done x
    
        type OkOrException<'T> =
            | Ok of 'T
            | Exception of System.Exception                    
    
        /// The catch for the computations. Stitch try/with throughout
        /// the computation and return the overall result as an OkOrException.
        let rec catch e =
            match e with
            | Done x -> result (Ok x)
            | NotYetDone work ->
                NotYetDone (fun () ->
                    let res = try Ok(work()) with | e -> Exception e
                    match res with
                    | Ok cont -> catch cont // note, a tailcall
                    | Exception e -> result (Exception e))
    
        /// The delay operator.
        let delay f = NotYetDone (fun () -> f())
    
        /// The stepping action for the computations.
        let step c =
            match c with
            | Done _ -> c
            | NotYetDone f -> f ()
    
        // The rest of the operations are boilerplate.
    
        /// The tryFinally operator.
        /// This is boilerplate in terms of "result", "catch" and "bind".
        let tryFinally e compensation =   
            catch (e)
            |> bind (fun res ->  compensation();
                                 match res with
                                 | Ok v -> result v
                                 | Exception e -> raise e)
    
        /// The tryWith operator.
        /// This is boilerplate in terms of "result", "catch" and "bind".
        let tryWith e handler =   
            catch e
            |> bind (function Ok v -> result v | Exception e -> handler e)
    
        /// The whileLoop operator.
        /// This is boilerplate in terms of "result" and "bind".
        let rec whileLoop gd body =   
            if gd() then body |> bind (fun v -> whileLoop gd body)
            else result ()
    
        /// The sequential composition operator
        /// This is boilerplate in terms of "result" and "bind".
        let combine e1 e2 =   
            e1 |> bind (fun () -> e2)
    
        /// The using operator.
        let using (resource: #System.IDisposable) f =
            tryFinally (f resource) (fun () -> resource.Dispose())
    
        /// The forLoop operator.
        /// This is boilerplate in terms of "catch", "result" and "bind".
        let forLoop (e:seq<_>) f =
            let ie = e.GetEnumerator()
            tryFinally (whileLoop (fun () -> ie.MoveNext())
                                  (delay (fun () -> let v = ie.Current in f v)))
                       (fun () -> ie.Dispose())
    
    
    // Give the mapping for F# computation expressions.
    type EventuallyBuilder() =
        member x.Bind(e,k)                  = Eventually.bind k e
        member x.Return(v)                  = Eventually.result v   
        member x.ReturnFrom(v)              = v   
        member x.Combine(e1,e2)             = Eventually.combine e1 e2
        member x.Delay(f)                   = Eventually.delay f
        member x.Zero()                     = Eventually.result ()
        member x.TryWith(e,handler)         = Eventually.tryWith e handler
        member x.TryFinally(e,compensation) = Eventually.tryFinally e compensation
        member x.For(e:seq<_>,f)            = Eventually.forLoop e f
        member x.Using(resource,e)          = Eventually.using resource e
    

提交回复
热议问题