Search code examples
f#elmish-wpf

Type Elmish.WPF main() function reports binding () -> #Window as an error


Why does my version of the Elmish.WPF Sample NewWindow (XAML code and F# Core) emit the error ...

  • The type 'unit -> 'a' is not compatible with the type 'Window'.

The same line emits the warning...

  • This construct causes code to be less generic than indicated by its type annotations. The type variable implied by the use of a '#', '_' or other type annotation at or near <line reference to createWindow_Window2 in the let bindings = statement">

Why am I getting this error and warning?

What I am doing is merging the Elmish.WPF Samples SingleCounter (XAML code and F# Core) and NewWindow (XAML code and F# Core) to have the Model, bindings(), and Msg parts in Program.fs instead of App.fs (as it was in the NewWindow sample).

My goal is to make a SimpleCounter able to open a NewWindow.

The XAML code passes Func<Window2> into the F# code here...

let main mainWindow (createWindow2: Func<#Window>)

...I define bindings as...

let bindings = Platform.bindings createWindow_Window2

I am down to the one compiler error I mentioned above (and that also appears in line beginning "let bindings = ..." below)...

let main mainWindow (createWindow_Window2: Func<#Window>) =

  let logger =
      LoggerConfiguration()
            .MinimumLevel.Override("Elmish.WPF.Update", Events.LogEventLevel.Verbose)
            .MinimumLevel.Override("Elmish.WPF.Bindings", Events.LogEventLevel.Verbose)
            .MinimumLevel.Override("Elmish.WPF.Performance", Events.LogEventLevel.Verbose)
            .WriteTo.Console()
            .CreateLogger()
  let createWindow_Window2 = 
      let window = createWindow_Window2.Invoke()
      window.Owner <- mainWindow
      window

  let bindings = Platform.bindings createWindow_Window2
  WpfProgram.mkProgramWithCmdMsg (fun _ -> m_init, []) update bindings toCmd
    |> WpfProgram.withLogger (new SerilogLoggerFactory(logger))
    |> WpfProgram.startElmishLoop mainWindow

The top of Program.bindings is defined as ...

 let bindings (createWindow_Window2: unit -> #Window) () : Binding<Model, Msg> list = [
        "Window_Window2_Show|> Binding.cmd Window_AboutProduct_Show
        "Window_Window2" |> Binding.subModelWin(
                                                Window_Window2.get >> WindowState.ofOption,
                                                snd,
                                                Window_Window2ct.mapInOutMsg,
                                                Window_Window2_Module.Window_Window2.bindings,
                                                createWindow_Window2,
                                                isModal = true)
...bindings continue but are not relevant to this question...

... and reports no errors!

The compiler seems to think Platform.bindings is expecting unit -> `a when createWindow_Window2 is correctly typed as unit -> #Window.

You can see the call into Program.fs:main is correctly called from App.xaml.cs with a lambda function returning a Window2 as follows...

        private void StartElmish(object sender, EventArgs e)
        {
            this.Activated -= StartElmish;
            Program.main(MainWindow, () => new Window2());
        }

So my question is why am I getting this mismatch type error when it seems I am passing the correct types?

Thank you!


Solution

  • A big thank you to: Brian Berns, Bent Tranberg, and Tomas Petricek for helping me!

    The answer to my problem was astonishingly simple to an expert but to a relative novice only yielded after meticulous comparison with the working Elmish.WPF code for NewWindow.

    For easy comparison I have pushed up to rfreytag/Elmish.WPF a version of the NewWindow (XAML code and F# Core) sample that compiles and runs (REMEMBER to build the NewWindow sample).

    This version of NewWindow (XAML code and F# Core) when compiled shows the error and warning I reported above …

    The type 'unit -> 'a' is not compatible with the type 'Window'. 
    See also C:\Workspace\Elmish.WPF\src\Samples\AnotherNewWindow.Core\Program.fs(78,72)-(78,79).
    

    ...and the warning...

    This construct causes code to be less generic than indicated by its 
    type annotations. The type variable implied by the use of a '#', '_' 
    or other type annotation at or near 
    'C:\Workspace\Elmish.WPF\src\Samples\AnotherNewWindow.Core\Program.fs(78,72)-(78,79)' 
    has been constrained to be type 'unit -> 'a'.
    

    To see the precise fix you can compare the working and breaking branches on my copy of Elmish.WPF. Which is that I had forgotten the () following the definition of let createWindow_Window2 = ...

    let createWindow_Window2 = 
          let window = createWindow_Window2.Invoke()
          window.Owner <- mainWindow
          window
    

    ... returns the function () -> Window while the correct ...

    let createWindow_Window2 () = 
          let window = createWindow_Window2.Invoke()
          window.Owner <- mainWindow
          window
    

    ... returns the needed Window.

    A newbie looks at unit → ‘a and doesn’t immediately recognize it as a function. And of course, functions are not C# Window objects.

    Did not help me that the error message doesn’t follow the F# form of...

    expecting a <type sought by context> 
    but was given a <passed in type>
    

    ...which has become familiar from working with the F# compiler.

    Not sure why this message diverged from that pattern. Maybe someone can explain that?

    The warning is alerting to the less-specific #Window (see # ‘flexible type’ definition) possibly clashing at run-time with the unit → ‘a function.

    As usual, learning a new framework means learning the compiler and linker messages.