Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updated ViewCE #263

Open
TheAngryByrd opened this issue Feb 15, 2023 · 0 comments
Open

Updated ViewCE #263

TheAngryByrd opened this issue Feb 15, 2023 · 0 comments

Comments

@TheAngryByrd
Copy link

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant