2009-03-21 14 views
9

Estoy intentando simular una interfaz en OCaml y estoy usando la construcción "tipo". Tengo dos tipos:OCaml tipos con diferentes niveles de especificidad

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

... y me gustaría definir un fooSansBar en particular:

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

... pero me han dicho que el campo de la barra no está definido. A partir de esto, parece que, al contrario de los valores que pasé en la coincidencia de la firma de fooSansBar, el sistema cree que estoy tratando de crear un fooConBar. ¿Es posible crear un fooSansBar si existen los dos tipos definidos anteriormente?

Además (porque soy nuevo en OCaml) ¿hay una mejor manera de simular una interfaz?

Respuesta

3

El segundo tipo redefine ayb, ocultando efectivamente el primero, por lo que no se puede construir más. Podría definir estos tipos en diferentes módulos, pero eso sería lo mismo que usar un nombre diferente para a y b.

Estas construcciones solo se pueden usar cuando no intenta "derivar" desde otra interfaz, sino simplemente implementarlo.

Si desea utilizar estos conceptos orientados a objetos en Ocaml, podría examinar el sistema de objetos o, según su problema, el sistema de módulos. Alternativamente, podría tratar de resolver su problema de una manera funcional. ¿Que problema estas tratando de resolver?

9

En OCaml, los nombres de campo en tipos de registro deben ser únicos, por lo que los dos tipos que defina no pueden coexistir simultáneamente. Caml es el único idioma que conozco con esta propiedad.

Como la segunda definición oculta la primera, cuando el compilador ve los campos a y b espera que pertenezcan al tipo fooConBar y se queja del campo de barra faltante.

Si está tratando de simular una interfaz, la forma correcta y funcional de hacerlo en Caml es definir un module type.

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

Y una instancia:

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

Con módulos y tipos de módulos que también consigue subtipos; no hay necesidad de recurrir a los objetos.

P.S. Mi Caml está oxidado; la sintaxis puede estar desactivada.

4

Existen varias soluciones posibles en OCaml dependiendo de cómo esté usando el código que proporcionó. El más simple es la combinación de los dos tipos: (! Y pueden tener sus tipos inferir lo que no hay necesidad de declarar un tipo)

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

Otra solución consiste en sustituir los registros con los objetos debido a objetos de soporte subtipificación:

# 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

En OCaml, no es posible tener dos tipos de registros con conjuntos de campos intersecantes presentes en el mismo ámbito.

Si realmente necesita usar los tipos de registro con la intersección de conjuntos de campos, entonces se puede evitar esta restricción encerrando los tipos dentro de sus propios módulos dedicados:

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

Entonces se puede construir instancias de estos tipos como así:

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

Estas instancias tienen los siguientes tipos:

fsb : FooSansBar.t 
fcb : FooConBar.t 
2

OCaml proporciona dos formas de implementar interfaces. Uno, como ya se mencionó, es un tipo de módulo.

El otro es un tipo de clase. Puede escribir un tipo de clase (interfaz) fooSansBar:

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

y un tipo de clase fooConBar:

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

Esto le permitirá utilizar un fooConBar en cualquier lugar se requiere un fooSansBar. Ahora puede crear un fooSansBar, utilizando el tipo de inferencia:

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

Ahora, el tipo fsb 's pasa a ser <a: string; b: int>, como se indica por Jon, pero es perfectamente utilizable como fooSansBar debido a subtipos estructural de OCaml.

Cuestiones relacionadas