2017-10-17 5 views
3

Je le code suivantComment transformer OO appels en une certaine fonction générique appelle

type Show<'a> = 
    abstract member Show: 'a -> string 

type Shows() = 
    member inline this.GetShow(x:string) = 
     {new Show<string> with member this.Show(x:string) = x} 
    member inline this.GetShow(x:int) = 
     {new Show<int> with member this.Show(x:int) = sprintf "%A" x} 

qui fonctionne parfaitement si je l'appelle en utilisant la notation normale OO.

printfn "100 %s" (Shows().GetShow("some").Show("some")) 

Cependant, j'aimerais conclure que dans une fonction afin que

let inline show x = (Shows().GetShow(x).Show(x)) 

Mais cela me donne l'erreur suivante

[FS0041] A unique overload for method 'GetShow' could not be determined based 
on type information prior to this program point. A type annotation may be 
needed. Candidates: 
member Shows.GetShow : x:int -> Show<int>, 
member Shows.GetShow : x:string -> Show<string> 

Toute idée comment surmonter cela?

Répondre

3

Est-ce que cela vous rapproche suffisamment de ce que vous voulez?

let inline GetShow p x = (^x : (member GetShow : ^p -> ^o) (x, p)) 
let inline Show p x = (^x : (member Show : ^p -> ^o) (x, p)) 

let inline show x s = s |> GetShow x |> Show x 

Shows() |> show "a" 
Shows() |> show 1 

Il est pas trop difficile si vous créez votre Shows en dehors de la fonction en ligne. De cette façon, les méthodes n'ont pas besoin d'être en ligne.

2

Vous devez utiliser des paramètres de type résolus statiquement et indiquer explicitement que le type doit avoir un membre GetShow avec la signature requise. En outre, cela ne fonctionne qu'avec les membres statiques.

type Shows() = 
    static member inline GetShow(x:string) = 
     {new Show<string> with member this.Show(x:string) = x} 
    static member inline GetShow(x:int) = 
     {new Show<int> with member this.Show(x:int) = sprintf "%A" x} 

let inline ($) (a: ^a) (b: ^b) = 
    ((^a or ^b): (static member GetShow : ^b -> Show< ^b>) b) 

let inline show x = (Shows() $ x).Show(x) 

la contrainte d'emballage dans un opérateur séparé $ est nécessaire, parce que vous ne pouvez spécifier des contraintes résolus statiquement sur les paramètres de type - à dire que vous ne pouvez pas dire quelque chose comme (when Show : (member ...)), ne peut pas utiliser le type de béton dans Show là, doit être un paramètre. Nous introduisons donc une fonction intermédiaire $ puis l'appelons Show comme paramètre.

Et la raison pour laquelle j'utilise un opérateur $ au lieu d'une fonction régulière est que les contraintes résolues statiquement sont déduites pour les opérateurs. Avec une fonction régulière, vous devez écrire deux fois la clause when ... - une fois dans la signature, une fois dans le corps.