Maslow
Maslow

Reputation: 18746

How do you define this statically resolved type parameter?

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

Answers (2)

piaste
piaste

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

Tomas Petricek
Tomas Petricek

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

Related Questions