2009-03-21 12 views
9

Je tente de simuler une interface dans OCaml et j'utilise la construction "type". J'ai deux types:Types OCaml avec différents niveaux de spécificité

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

... et je voudrais définir un fooSansBar particulier:

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

... mais on me dit que le champ de la barre n'est pas défini. À partir de là, il semble que, contrairement aux valeurs que j'ai réussi à faire correspondre la signature de fooSansBar, le système pense que j'essaie de créer un fooConBar. Est-il possible de créer un fooSansBar si les deux types définis ci-dessus existent?

En outre (parce que je suis nouveau à OCaml) existe-t-il une meilleure façon de simuler une interface?

Répondre

3

Le deuxième type redéfinit a et b, cachant effectivement le premier, ce qui explique pourquoi il ne peut plus être construit. Vous pouvez définir ces types dans différents modules, mais cela revient à utiliser un nom différent pour a et b.

Ces constructions ne peuvent être utilisées que si vous ne tentez pas de "dériver" à partir d'une autre interface, mais simplement l'implémenter.

Si vous souhaitez utiliser ces concepts orientés objet dans Ocaml, vous pouvez consulter le système d'objets ou, selon votre problème, le système de modules. Alternativement, vous pouvez essayer de résoudre votre problème de manière fonctionnelle. Quel problème essayez-vous de résoudre?

9

Dans OCaml, les noms de champ dans les types d'enregistrement doivent être uniques, de sorte que les deux types que vous définissez ne peuvent pas coexister simultanément. Caml est la seule langue que je connaisse avec cette propriété. Étant donné que la deuxième définition masque la première, lorsque le compilateur voit les champs a et b, il s'attend à ce qu'ils appartiennent au type fooConBar et se plaint donc du champ barre manquante

Si vous essayez de simuler une interface, la manière fonctionnelle correcte de le faire dans Caml est de définir un module type.

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

Et un exemple:

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

Avec les modules et les types de modules vous obtenez également sous-typage; il n'y a pas besoin de recourir à des objets.

P.S. Mon Caml est rouillé; la syntaxe peut être désactivée.

4

Il existe plusieurs solutions possibles dans OCaml selon la façon dont vous utilisez le code que vous avez donné. Le plus simple est de combiner les deux types: (! Et peuvent avoir leurs types inférés donc il n'y a pas besoin de déclarer un type)

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

Une autre solution consiste à remplacer les enregistrements avec des objets parce que les objets prennent en charge le sous-typage:

# 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

Dans OCaml, il n'est pas possible d'avoir deux types d'enregistrement avec des ensembles de champs croisés présents dans la même étendue.

Si vous avez vraiment besoin d'utiliser les types d'enregistrements avec intersection jeux sur le terrain, alors vous pouvez contourner cette restriction en enfermant les types dans leurs propres modules dédiés:

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

Ensuite, vous pouvez construire des instances de ces types comme si:

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

Ces instances ont les types suivants:

fsb : FooSansBar.t 
fcb : FooConBar.t 
2

OCaml fournit deux façons d'implémenter des interfaces. Un, comme déjà mentionné, est un type de module.

L'autre est un type de classe. Vous pouvez écrire un type de classe (interface) fooSansBar:

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

et un type de classe fooConBar:

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

Cela vous permettra d'utiliser un fooConBar où un fooSansBar est nécessaire. Vous pouvez maintenant créer un fooSansBar, en utilisant l'inférence de type:

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

Maintenant, le type de fsb se trouve être <a: string; b: int>, comme indiqué par Jon, mais il est parfaitement utilisable comme fooSansBar en raison de sous-typage structurel de OCaml.