This looks interesting. In converting this to work with WPF I ran into a couple of bumps. Obviously WPF doesn't have a single pattern of composition, so the abstract class is less useful. But a more worrying problem arrived when I tried to add do! to the computation expression builder. This code works fine:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
open System.Windows
open System.Windows.Controls

 
[<AbstractClass>]
type IComposableControl<'a> when 'a :> FrameworkElement () =
    abstract Control : 'a
    abstract Bind : FrameworkElement * (FrameworkElement -> 'a) -> 'a
    member this.Return (e: unit)  = this.Control
    member this.Zero () = this.Control


type WindowBuilder() =
    // Using type inference IComposableControl<_> causes Error: anonymous type variables are not permitted in this declaration.
    inherit IComposableControl<Window>() 

    let win = Window(Topmost=true)
    override this.Control = win
    override this.Bind(c: FrameworkElement, body) =
        win.Content <- c
        body c


type PanelBuilder(panel: Panel) =
    inherit IComposableControl()
    override this.Control = panel
    override this.Bind(c: FrameworkElement, body) =
        panel.Children.Add(c) |> ignore
        body c


let win =
    WindowBuilder() 
        {   let! panel = 
                PanelBuilder(StackPanel())
                    {   let! btn1 = Button(Content = "Hello")
                        let! btn2 = Button(Content = "World")
                        return () }
            return () }


win.Show() // Pops up the window in FSI.

In trying to add do! I get an overload error:

1
2
3
4
5
6
7
8
9
[<AbstractClass>]
type IComposableControl<'a> when 'a :> FrameworkElement () =
    abstract Control : 'a
    [<OverloadID("1")>]
    abstract Bind : FrameworkElement * (FrameworkElement -> 'a) -> 'a
    [<OverloadID("2")>]
    abstract Bind : FrameworkElement * (unit -> 'a) -> 'a
    member this.Return (e: unit)  = this.Control
    member this.Zero () = this.Control

Error: The method 'Bind' is overloaded. Possible matches are shown below (or in the Error List window)

It seems that the type inference can't figure out which Bind method to use. I also noticed that there were no warnings generated when I used incorrect OverloadIDs on the derived builders.

I could not seem to resolve this error. Am I getting the syntax wrong here? Otherwise a verification would be useful.

thanks,

Danny
By on 6/30/2009 4:24 AM ()
Hi Danny:

I'm floundering too. Not very familiar with type constraints, especially when combined with computation builders and abstract classes. Not too sure about how you expect to bind things with (unit -> 'a), so I changed it to (IComposableControl->'b), in keeping with the original. Here's what I came up with, but it generates a couple of internal compiler errors (see below):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#r "WindowsBase.dll"
#r "PresentationCore.dll"
#r "PresentationFramework.dll"

open System.Windows
open System.Windows.Controls
 
[<AbstractClass>]
type IComposableControl<'a> when 'a :> FrameworkElement () =
    abstract Control : 'a
    [<OverloadID("1")>]
    abstract Bind : FrameworkElement * (FrameworkElement -> 'a) -> 'a
    [<OverloadID("2")>]
    abstract Bind : IComposableControl<'b> * (FrameworkElement -> 'a)  -> 'a 
    member this.Return (e: unit)  = this.Control
    member this.Zero () = this.Control

type WindowBuilder() =
    inherit IComposableControl<Window>() 
    let win = Window(Topmost=true)
    override this.Control = win
    [<OverloadID("1")>]
    override this.Bind(c: FrameworkElement, body) =
        win.Content <- c
        body c
    [<OverloadID("2")>]
    override this.Bind(c: IComposableControl<'b>, body) : Window =
        win.Content <- c.Control
        body c.Control

type PanelBuilder(panel: Panel) =
    inherit IComposableControl()
    override this.Control = panel
    [<OverloadID("1")>]
    override this.Bind(c: FrameworkElement, body) =
        panel.Children.Add(c) |> ignore
        body c
    [<OverloadID("2")>]
    override this.Bind(c: IComposableControl<'b>, body) : Panel =
        panel.Children.Add(c.Control) |> ignore
        body c.Control

let win =
    WindowBuilder() 
        {   let! panel = 
                PanelBuilder(StackPanel())
                    {   let! btn1 = Button(Content = "Hello")
                        let! btn2 = Button(Content = "World")
                        return () }
            return () }

win.Show() // Pops up the window in FSI.

And the errors:

Error 1 internal error: dest_typar_typ: not a typar type (Failure) Please build a small example that reproduces this problem and report it to fsbugs@microsoft.com. script2.fsx 31 6 Miscellaneous Files
Error 2 internal error: dest_typar_typ: not a typar type Please build a small example that reproduces this problem and report it to fsbugs@microsoft.com. script2.fsx 1 1 Miscellaneous Files

By on 6/30/2009 9:10 AM ()
Brian @ fsbugs replies with the following. Tested as working. Thanks Brian!

Thanks for the report; the internal error has been fixed in our internal development bits, and I expect the code below may work on Beta1 (haven’t tried it there). The main change I made is just being more explicit in the type signatures.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#if INTERACTIVE
#r "WindowsBase.dll"
#r "PresentationCore.dll"
#r "PresentationFramework.dll"
#r "System.Xaml"
#endif

open System
open System.Windows
open System.Windows.Controls

[<AbstractClass>]
type IComposableControl<'a when 'a :> FrameworkElement> () =
    abstract Control : 'a
    abstract Bind : FrameworkElement * (FrameworkElement -> 'a) -> 'a
    abstract Bind : IComposableControl<'b> * (FrameworkElement -> 'a)  -> 'a 
    member this.Return (e: unit)  = this.Control
    member this.Zero () = this.Control

type WindowBuilder() =
    inherit IComposableControl<Window>() 
    let win = Window(Topmost=true)
    override this.Control = win
    override this.Bind(c: FrameworkElement, body: FrameworkElement -> Window) : Window =
        win.Content <- c
        body c
    override this.Bind(c: IComposableControl<'b>, body: FrameworkElement -> Window) : Window =
        win.Content <- c.Control
        body c.Control

type PanelBuilder(panel: Panel) =
    inherit IComposableControl()
    override this.Control = panel
    override this.Bind(c: FrameworkElement, body: FrameworkElement -> Panel) : Panel=
        panel.Children.Add(c) |> ignore
        body c
    override this.Bind(c: IComposableControl<'b>, body: FrameworkElement -> Panel) : Panel=
        panel.Children.Add(c.Control) |> ignore
        body c.Control


let win =
    WindowBuilder() 
        {   let! panel = 
                PanelBuilder(StackPanel())
                    {   let! btn1 = Button(Content = "Hello")
                        let! btn2 = Button(Content = "World")
                        return () }
            return () }

win.Show() // Pops up the window in FSI.

#if COMPILED
[<STAThread()>]
do 
    let app =  new Application() in
    app.Run() |> ignore
#endif


By on 7/1/2009 8:08 AM ()
IntelliFactory Offices Copyright (c) 2011-2012 IntelliFactory. All rights reserved.
Home | Products | Consulting | Trainings | Blogs | Jobs | Contact Us
Built with WebSharper