Skip to content

Updated ViewCE #263

@TheAngryByrd

Description

@TheAngryByrd

👋 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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions