ui
ui copied to clipboard
Updated ViewCE
👋 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<int>) = viewCE { // View<int>
/// 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<int>) = viewCE { // View<int>
/// 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.