Come accennato da Rémi, il problema con il codice è che il tipo di OCaml il sistema supporta solo un tipo per espressione: un'espressione di tipo sub
non è di tipo super
. Nel tuo esempio, myFunction
prevede un argomento di tipo super
e l'espressione new sub
è di tipo sub
, quindi il problema.
L'upcasting è essenziale per la programmazione orientata agli oggetti e OCaml lo supporta con due distinti costrutti.
Il primo è tipo coercizione. Se super
è un supertipo di sub
(nel senso che semanticamente, i valori di tipo sub
si comportano come valori di super
) e x : sub
, quindi (x :> super) : super
. L'operatore di tipo :>
rende esplicita la conversione — l'equivalente di ciò che i linguaggi popolari orientati agli oggetti implicano implicitamente quando si utilizza il valore di tipo sub
in cui è previsto super
.
Il secondo è vincoli supertype: richiede che una determinata variabile di tipo sia un sottotipo di un determinato tipo.Questo è scritto come #super
o (#super as 'a)
se si desidera nominare la variabile di tipo all'interno. I vincoli Supertype in realtà non cambiano il tipo di espressione come fa la coercizione di tipo, ma controllano semplicemente che il tipo sia un sottotipo valido del tipo richiesto.
a diventare più consapevoli della differenza, si consideri il seguente esempio:
class with_size ~size = object
val size = size : int
method size = size
end
class person ~name ~size = object
inherit with_size ~size
val name = name : string
method name = name
end
let pick_smallest_coerce (a : with_size) (b : with_size) =
if a # size < b # size then a else b
let pick_smallest_subtype (a : #with_size) (b : #with_size) =
if a # size < b # size then a else b
Il tipo di pic_smallest_coerce
è with_size -> with_size -> with_size
: anche se hai superato due person
casi, il valore di ritorno sarebbe di tipo with_size
e si sarebbe non essere in grado di chiamare il suo metodo name
.
Il tipo di pic_smallest_subtype
è (#with_size as 'a) -> 'a -> 'a
: se passate due person
casi, il sistema di tipo determinerebbe che 'a = person
e identificare correttamente il valore restituito come essendo di tipo person
(che consente di utilizzare il metodo name
).
In breve, i vincoli Supertype semplicemente assicurarsi che il codice verrà eseguito, senza perdere alcuna informazione di tipo affatto — la variabile mantiene il suo tipo di originale. Tipo coercizione perde effettivamente digitare le informazioni (che, in assenza di down-casting, è una cosa molto brutta), in modo da dovrebbe essere utilizzato solo come ultima risorsa in due situazioni:
1. Non è possibile avere una funzione polimorfica. I vincoli Supertype si basano su #super
come variabile di tipo libero, quindi se non puoi permetterti di avere una variabile di tipo libero nel tuo codice, dovrai farne a meno.
2. È necessario in realtà negozio valori di diversi tipi effettivi nello stesso contenitore. Un elenco o di riferimento che può contenere sia person
o box
casi utilizzeranno with_size
e coercizione:
let things = [ my_person :> with_size ; my_box :> with_size ]
si noti che l'algoritmo di inferenza scoprirà vincoli Supertype di propria (non determinerà quale classe o di classe che si Tipo intendeva usare, ma si costruirà un tipo di classe letterale):
let pick_smallest_infer a b =
if a # size < b # size then a else b
val pick_smallest_infer : (< size : 'a ; .. > as 'b) -> 'b -> 'b
come tale, con rare eccezioni, l'annotazione dei vincoli Supertype reale è un esercizio utile solo quando documentare il codice.
'sub' è un sottotipo di' super', poiché l'aggiunta di un metodo non impedisce di violare il contratto richiesto da 'super'. Come sottolinea Rémi, tuttavia, OCaml non esegue la coercizione automatica. –