ui icon indicating copy to clipboard operation
ui copied to clipboard

Updated ViewCE

Open TheAngryByrd opened this issue 2 years ago • 0 comments

👋 I mentioned I have a viewCE in the F# Foundation slack and @granicz was curious about it:


[<AutoOpen>]
module CE =
    open WebSharper
    open WebSharper.JavaScript
    open WebSharper.UI
    open System.Collections.Generic
    /// <summary>
    /// Builds a view using a computation expression. This can bind against Views, Vars, Asyncs, and Promises.
    /// </summary>
    /// <example>
    /// <code lang="fsharp">
    /// let myView (myVar : Var&lt;int&gt;) = viewCE { // View&lt;int&gt;
    ///     let! result1 = View.Const 42
    ///     and! result2 = myVar
    ///     and! result3 = async { return 1701 }
    ///     and! result4 = promise { return 1138 }
    ///     return result1 * result2 + result3 - result4
    /// }
    /// </code>
    /// </example>
    type ViewBuilder () =
        /// Called for return in computation expressions.
        [<Inline>]
        member inline this.Return (v) =
            View.Const v
        /// Called for return! in computation expressions.
        [<Inline>]
        member inline this.ReturnFrom (v : View<_>) =
            v
        /// Called for let! and do! in computation expressions.
        [<Inline>]
        member inline this.Bind (x : View<'a>, [<InlineIfLambda>] f : 'a -> View<'b>) =
            View.Bind f x
        /// Called for efficient let! and and! in computation expressions without merging inputs.
        [<Inline>]
        member inline this.Bind2 (x , y , [<InlineIfLambda>] f : 'a * 'b -> View<'c>) =
            View.Join(View.Map2 (fun x y -> f (x,y)) x y )
        /// Called for efficient let! and and! in computation expressions without merging inputs.
        [<Inline>]
        member inline this.Bind3 (x : View<'a>, y : View<'b>, z : View<'c>, [<InlineIfLambda>] f : 'a * 'b * 'c -> View<'d>) =
            View.Join(View.Map3 (fun x y z -> f (x, y, z)) x y z)
        /// Called for an efficient let! ... return in computation expressions.
        [<Inline>]
        member inline this.BindReturn (x : View<'a>, [<InlineIfLambda>] f : 'a -> 'b) =
            View.Map f x
        /// Called for efficient let! ... and! ... return in computation expressions without merging inputs.
        [<Inline>]
        member inline this.Bind2Return (x : View<'a>, y : View<'b>, [<InlineIfLambda>] (f: 'a * 'b -> 'm)) =
            View.Map2 (fun x y -> f (x,y)) x y
        /// Called for efficient let! ... and! ... return in computation expressions without merging inputs.
        [<Inline>]
        member inline this.Bind3Return (x, y, z, [<InlineIfLambda>] (f: 'i * 'j * 'k -> 'l)) =
            View.Map3 (fun x y z -> f(x,y,z)) x y z
        /// Called for and! in computation expressions.
        [<Inline>]
        member inline this.MergeSources (v1 : View<'a>, v2 : View<'b>) =
            View.Map2 (fun x y -> x,y) v1 v2
        /// Called for and! in computation expressions, but improves efficiency by reducing the number of tupling nodes.
        [<Inline>]
        member inline this.MergeSources3 (v1 : View<'a>, v2 : View<'b>, v3 : View<'c>) =
            View.Map3 (fun x y z -> x,y,z) v1 v2 v3
        /// Called for empty else branches of if...then expressions in computation expressions.
        [<Inline>]
        member inline this.Zero () =
            this.Return ()
        /// Called for sequencing in computation expressions.
        [<Inline>]
        member inline this.Combine (
                result: View<unit>,
                [<InlineIfLambda>] binder: unit -> View<_> ) : View<_> =
            this.Bind(result, binder)
        /// Wraps a computation expression as a function. Delayed<'T> can be any type, commonly M<'T> or unit -> M<'T> are used.
        /// The default implementation returns a M<'T>.
        [<Inline>]
        member inline this.Delay ([<InlineIfLambda>] generator : unit -> View<_>) =
            generator
        /// Executes a computation expression.
        [<Inline>]
        member inline _.Run
            ([<InlineIfLambda>] generator: unit -> View<_>) =
                generator ()
        /// Called for try...with expressions in computation expressions.
        [<Inline>]
        member inline this.TryWith (
            [<InlineIfLambda>] generator : unit -> View<_>,
            [<InlineIfLambda>] handler : exn -> View<_>) =
            try
                this.Run generator
            with e ->
                handler e
        /// Called for try...finally expressions in computation expressions.
        [<Inline>]
        member inline this.TryFinally (
            [<InlineIfLambda>] generator : unit -> View<_>,
            [<InlineIfLambda>]compensation  : unit -> unit) =
            try
                this.Run generator
            finally
                compensation ()
        /// Called for use bindings in computation expressions.
        [<Inline>]
        member inline this.Using (
                resource: 'disposable :> System.IDisposable,
                [<InlineIfLambda>]binder: 'disposable -> View<_>)  =
            this.TryFinally(
                (fun () -> binder resource),
                (fun () ->
                    if not (obj.ReferenceEquals(resource, null)) then
                        resource.Dispose()
                )
            )

        // `Source` members allows the computation expression to turn other types into a View
        /// This is the identity Source member
        [<Inline>]
        member inline this.Source(v : View<'a>) =
            v
        /// This is the identity Source member for For/While loops
        [<Inline>]
        member inline this.Source(v : #seq<_>) =
            v
    [<AutoOpen>]
    module Extensions =
        type ViewBuilder with
            /// Coverts Async to View
            [<Inline>]
            member inline this.Source(v : Async<_>) =
                View.ConstAsync v
            /// Converts Var to View
            [<Inline>]
            member inline this.Source(v : Var<_>) =
                View.FromVar v
            /// Converts Promise to View
            [<Inline>]
            member inline this.Source(v : Promise<_>) =
                this.Source(v.AsAsync())

    /// <summary>
    /// Builds a view using a computation expression. This can bind against Views, Vars, Asyncs, and Promises.
    /// </summary>
    /// <example>
    /// <code lang="fsharp">
    /// let myView (myVar : Var&lt;int&gt;) = viewCE { // View&lt;int&gt;
    ///     let! result1 = View.Const 42
    ///     and! result2 = myVar
    ///     and! result3 = async { return 1701 }
    ///     and! result4 = promise { return 1138 }
    ///     return result1 * result2 + result3 - result4
    /// }
    /// </code>
    /// </example>
    let viewCE = ViewBuilder()
    

I wasn't able to implement While/For loops as they seem to stackoverflow for large numbers (I'm also on 6.0.0.228 so maybe there's a fix for this in later versions.)

I also haven't implemented an exhaustive test suite so there maybe be intricacies I'm not aware of.

TheAngryByrd avatar Feb 15 '23 12:02 TheAngryByrd