Reputation: 18746
given
open System
open System.Windows
open System.Windows.Input
open System.ComponentModel
type RelayCommand (canExecute:(obj -> bool), action:(obj -> unit)) =
let event = new DelegateEvent<EventHandler>()
interface ICommand with
[<CLIEvent>]
member x.CanExecuteChanged = event.Publish
member x.CanExecute arg = canExecute(arg)
member x.Execute arg = action(arg)
member x.CheckCanExecute (sender:obj) (eventArgs:EventArgs) = event.Trigger([| sender;eventArgs |])
how do I write a statically resolved type parameterized function that can satisfy a call to CheckCanExecute
?
while this function works it doesn't help me learn statically resolved type parameter syntax
let checkCanExecute (c:RelayCommand) = c.CheckCanExecute (box this) (EventArgs())
I expected this to work
let checkCanExecute (e:^a) = (^a: (member CheckCanExecute: sender:obj -> EventArgs -> unit ) (e, (box me),(EventArgs())))
but at the callsite
checkCanExecute addCommand
I get method or object constructor 'CheckCanExecute' not found (when using the 2nd definition, the 1st compiles just fine)
how do I define a class let
binding (or member binding if that is a better way to get the job done) that uses Statically Resolved Type Parameters to be able to call the method on anything that has the matching method signature?
Upvotes: 3
Views: 756
Reputation: 2058
If you want to use SRTPs, you cannot curry methods the ordinary way. The member invocation syntax is unable to handle multiple curried arguments at once.
Either you go with .NET-style tupled declarations, as Tomas suggests, or alternatively you must explicitly write out the currying in the form member x.f a = fun b -> fun c -> ...
.
In your example this would mean:
type RelayCommand
// ...
member x.CheckCanExecute sender = fun eventArgs ->
event.Trigger([| sender;eventArgs |])
let inline checkCanExecute (e:^a) =
(^a: (member CheckCanExecute: obj -> (EventArgs -> unit)) (e, (box e)) ) <| EventArgs()
Upvotes: 6
Reputation: 243041
I think some of the difficulty here is caused by the fact that CheckCanExecute
is defined as a curried function. For members, it is probably better to go with a tupled function (the way curried functions are compiled is tricky and it might be confusing the statically resolved constraint).
If you change the RelayCommand
member as follows:
member x.CheckCanExecute (sender:obj, eventArgs:EventArgs) =
event.Trigger([| sender;eventArgs |])
And make your checkCanExecute
an inline
function that requires tupled function:
let inline checkCanExecute (e:^a) =
(^a: (member CheckCanExecute: obj * EventArgs -> unit ) (e, box me,(EventArgs())))
Then the following type checks:
checkCanExecute me
Upvotes: 6