2009-03-21 5 views
9

Sto tentando di simulare un'interfaccia in OCaml e sto usando il costrutto "tipo". Ho due tipi:Tipi di OCaml con diversi livelli di specificità

type fooSansBar = {a: string; b: int};; 
type fooConBar = {a:string; b:int; bar:char};; 

... e vorrei definire un particolare fooSansBar:

let fsb = {a="a"; b=3};; 

... ma è stato detto che il campo bar non è definito. Da questo, sembra che, contrariamente ai valori che ho passato nella corrispondenza della firma di fooSansBar, il sistema crede che sto cercando di creare un fooConBar. È possibile creare un fooSansBar se esistono i due tipi sopra definiti?

In aggiunta (perché sono nuovo di OCaml) c'è un modo migliore per simulare un'interfaccia?

risposta

3

Il secondo tipo ridefinisce a e b, nascondendo efficacemente il primo, motivo per cui non può più essere costruito. Potresti definire questi tipi in diversi moduli, ma sarebbe lo stesso che usare un nome diverso per a e b.

Questi costrutti possono essere utilizzati solo quando non si tenta di "derivare" da un'altra interfaccia, ma solo implementarla.

Se si desidera utilizzare questi concetti orientati agli oggetti in Ocaml, è possibile esaminare il sistema dell'oggetto o, a seconda del problema, il sistema del modulo. In alternativa, potresti provare a risolvere il tuo problema in modo funzionale. Che problema stai cercando di risolvere?

9

In OCaml, i nomi dei campi nei tipi di record devono essere univoci, quindi i due tipi definiti non possono coesistere simultaneamente. Caml è l'unica lingua che conosco con questa proprietà.

Poiché la seconda definizione nasconde la prima, quando il compilatore vede i campi aeb si aspetta che appartengano al tipo fooConBar e quindi si lamenta del campo della barra mancante.

Se si sta tentando di simulare un'interfaccia, il modo corretto per farlo in Caml è definire un module type.

module type FOO_CON_BAR = sig 
    val a : string 
    val b : int 
    val bar : char 
end 

E un'istanza:

module Example = struct 
    let a = "hello" 
    let b = 99 
    let c = '\n' 
end 

Con i moduli e tipi di moduli si ottiene anche sottotipo; non è necessario ricorrere agli oggetti.

P.S. My Caml è arrugginito; la sintassi potrebbe essere disattivata.

4

Ci sono diverse soluzioni possibili in OCaml a seconda di come stai usando il codice che hai fornito. Il più semplice è quello di combinare i due tipi: (! E possono avere i loro tipi dedurre quindi non c'è bisogno di dichiarare un tipo)

type fooBar = { a: string; b: int; bar: char option } 

Un'altra soluzione è quella di sostituire i record con gli oggetti, perché gli oggetti di supporto sottotipizzazione:

# let fsb = object 
    method a = "a" 
    method b = 3 
    end;; 
val fsb : < a : string; b : int > = <obj> 

# fsb#a, fsb#b;; 
- : string * int = ("a", 3) 
1

In OCaml, non è possibile avere due tipi di record con set di campi intersecanti presenti nello stesso ambito.

Se davvero bisogno di usare tipi di record con intersecano impostazioni di campo, allora è possibile aggirare questa restrizione racchiudendo i tipi all'interno dei propri moduli dedicati:

module FooSansBar = struct type t = {a:string; b:int} end 
module FooConBar = struct type t = {a:string; b:int; bar:char} end 

Quindi è possibile costruire istanze di questi tipi come così:

let fsb = {FooSansBar.a="a"; b=3} 
let fcb = {FooConBar.a="a"; b=4; bar='c'} 

Queste istanze hanno i seguenti tipi:

fsb : FooSansBar.t 
fcb : FooConBar.t 
2

OCaml offre due modi per implementare le interfacce. Uno, come già detto, è un tipo di modulo.

L'altro è un tipo di classe. È possibile scrivere un tipo di classe (interfaccia) fooSansBar:

class type fooSansBar = object 
    method a: string 
    method b: int 
end 

e un tipo di classe fooConBar:

class type fooConBar = object 
    inherit fooSansBar 
    method bar: char 
end 

questo vi permetterà di utilizzare un fooConBar ovunque un fooSansBar è richiesto. È ora possibile creare un fooSansBar, utilizzando inferenza di tipo:

let fsb = object 
    method a = "a" 
    method b = 3 
end 

Ora, tipo fsb s' sembra essere <a: string; b: int>, come indicato da Jon, ma è perfettamente utilizzabile come fooSansBar a causa di sottotipizzazione strutturale di OCaml.