简体   繁体   中英

Can I specify a function type on F# member functions?

I'm currently playing around with using Eto.Forms from F#. One minor annoyance I've run into is that when you define your GUI objects (Forms, Panels, Buttons, etc) in external files (XAML or JSON) and declare event handlers, those event handlers have to have a specific type:

member public this.OnFirstButtonClicked (sender:Object, e:EventArgs) ->
    MessageBox.Show(this, "First button was clicked")
    |> ignore
member public this.OnSecondButtonClicked (sender:Object, e:EventArgs) ->
    MessageBox.Show(this, "Second button was clicked")
    |> ignore

The repetition of the type signatures is bothering me. (There's actually a lot of repetition in these two functions, like the calls to MessageBox with barely-varying parameters, but this is just a test project. In a real project, I'd be doing something different for both these buttons.) I'd like to not have to repeat the type signature of each of these functions every time. After reading this page at F Sharp For Fun and Profit , I thought I could do something like this:

type EventHandler = (Object * EventArgs) -> unit

member public this.OnFirstButtonClicked : EventHandler ->
    MessageBox.Show(this, "First button was clicked")
    |> ignore
member public this.OnSecondButtonClicked : EventHandler ->
    MessageBox.Show(this, "Second button was clicked")
    |> ignore

However, when I tried this I discovered that on member functions, that syntax actually means "This function returns an EventHandler function". I want to say "This member function is an EventHandler function", and I don't know how to do that.

Update: Since writing the above question, I've learned that I don't actually have to specify the type signatures of the event handler functions' parameters. The following will work:

member public this.OnFirstButtonClicked (sender, e) ->
    MessageBox.Show(this, "First button was clicked")
    |> ignore
member public this.OnSecondButtonClicked (sender, e) ->
    MessageBox.Show(this, "Second button was clicked")
    |> ignore

However, my real question isn't "how can I make these event handlers work?" My real question is, "I've learned how to specify the type of a function that isn't a member of a class, but how do I do that for a function that is a member of a class?"

I am going to use some simpler types, but the idea here is the same.

Consider a simple type:

type t() = member x.Test()  = ();;

here Test clearly has type unit -> unit

Now, we try to rewrite this to avoid the ()

type t() = member x.Test : unit -> unit  = ();;

but this will fail because the right hand side has the incorrect value.

Here is one solution:

type t() = member x.Test : unit -> unit  = fun () -> () ;;

One solution you might try to get closer to what you want is to use _ for the ignored parameter:

type t() = member x.Test _ : unit -> unit  =  () ;;

but this won't work, as we are now a function that takes a single argument and returns a function.

At this point, what we actually want is a Signature file that will allow specifying the type signature of a member. Alternatively you could try an interface.

A final method is to move the arguments inside the function - then it would become

type t() = member x.Test : some signature = fun .....

The only difference between standalone function definition and method definition is the member keyword and self-identifier. Other than that, the syntax is the same.

Check again the example on F# for fun and profit

type AdditionFunction = int->int->int
let f:AdditionFunction = fun a b -> a + b

It's actually a value binding, not a function binding. The function is defined with a lambda expression fun ab -> a + b and bound to identifier f.

To use this kind of definition, you would have to define your event handlers as

type EventHandler = (Object * EventArgs) -> unit

member public this.OnFirstButtonClicked : EventHandler =
    fun (_, _) ->
        MessageBox.Show(this, "First button was clicked")
        |> ignore

Again, it works exactly the same for standalone functions.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM