2015-05-18 20 views
6

Si prega di spiegare la magia dietro la funzione drawShape. 1) Perché funziona a tutti - intendo come chiama il membro Draw, 2) perché deve essere inline?Come funziona questa annotazione di tipo e perché l'altra no?

type Triangle() = 
    member x.Draw() = printfn "Drawing triangle" 

type Rectangle() = 
    member x.Draw() = printfn "Drawing rectangle" 

let inline drawShape (shape : ^a) = 
    (^a : (member Draw : unit->unit) shape) 

let triangle = Triangle() 
let rect = Rectangle() 

drawShape triangle 
drawShape rect 

E il prossimo numero è - è possibile scrivere drawShape funzione utilizzando tipo di parametro di annotazione come qui di seguito? Ho scoperto che ha esattamente la stessa firma del primo, ma non sono in grado di completare il corpo.

let inline drawShape2 (shape : ^a when ^a : (member Draw : unit->unit)) = 
    ... 

Grazie in anticipo.

+0

la magia è tutta nel compilatore F # - non si sa più cosa dire - per il secondo problema: senza vedere il corpo/errore è abbastanza difficile dire a – Carsten

+0

Ho provato a inserire 'shape.Draw()' nel corpo senza successo. L'errore è: 'errore FS0072: ricerca su oggetto di tipo indeterminato basato su informazioni precedenti a questo punto programma. Potrebbe essere necessaria un'annotazione di tipo prima di questo punto del programma per vincolare il tipo dell'oggetto. Questo può consentire di risolvere la ricerca. – Endrju

+0

ah vedo - be ', purtroppo, devi ripetere la stessa cosa che hai fatto in 'drawShap' ancora una volta (la parte all'interno di' (^ a: (membro Draw: ...) forma) ' – Carsten

risposta

12

Questa sintassi Voodoo è denominata "statically resolved type parameter". L'idea è di chiedere al compilatore di verificare che il tipo passato come argomento generico abbia alcuni membri su di esso (nell'esempio - Draw).

Poiché CLR non supporta tali controlli, devono essere eseguiti in fase di compilazione, che il compilatore F # è felice di fare per voi, ma ha anche un prezzo: perché non c'è supporto CLR, non c'è modo di compilare tale funzione in IL, il che significa che deve essere "duplicato" ogni volta che viene utilizzato con un nuovo argomento generico (questa tecnica è anche nota come "monomorphisation"), ed è ciò che la parola chiave inline è per.

Come per la sintassi del chiamante: per qualche ragione, solo dichiarare il vincolo sul parametro stesso non lo taglia. È necessario dichiarare ogni volta che in realtà riferimento nella bacheca di:

// Error: "x" is unknown 
let inline f (a: ^a when ^a: (member x: unit -> string)) = a.x() 

// Compiles fine 
let inline f a = (^a: (member x: unit -> string)(a)) 

// Have to jump through the same hoop for every call 
let inline f (a: ^a) (b: ^a) = 
    let x = (^a: (member x: unit -> string)(a)) 
    let y = (^a: (member x: unit -> string)(b)) 
    x+y 

// But can wrap it up if it becomes too messy 
let inline f (a: ^a) (b: ^a) = 
    let callX t = (^a: (member x: unit -> string) t) 
    (callX a) + (callX b) 

// This constraint also implicitly carries over to anybody calling your function: 
> let inline g x y = (f x y) + (f y x) 
val inline g : x: ^a -> y: ^a -> string when ^a : (member x : ^a -> string) 

// But only if those functions are also inline: 
> let g x y = (f x y) + (f y x) 
Script.fsx(49,14): error FS0332: Could not resolve the ambiguity inherent in the use of the operator 'x' at or near this program point. Consider using type annotations to resolve the ambiguity. 
+0

Grazie per la spiegazione e per i link. Quindi, concludendo, questo '(^ a: (membro x: unità -> stringa) (a)) significa approssimativamente" assicurati che il tipo '^ a' contenga membro' x' che è una funzione senza parametro che restituisce stringa, e chiama Ovviamente 'a' senza argomenti – Endrju

+0

Ovviamente tutte le funzioni negli esempi precedenti devono essere' inline' :-) – Endrju

+0

Destra, totalmente dimenticato, grazie: -) –

Problemi correlati