3

私はOcamlで実装しようとしているこのOOの状況を持っています:2つのクラスX1X2、両方ともサブタイピングXX1 <: Xと)であり、またはのいずれかになるX2 <: Xを動的に返す関数を書きたいです。XX1X2

しかし、通常はOcamlのクラスを避けて代わりにモジュールを使用するのが良いと聞いたので、このように問題を表現しようとしています(過度に単純化されていますが、それでも重要です):2つのモジュールX1X2、そして関数が動的にどちらかを決定するようにしたいX1.tまたはのいずれかを返しますX2.t

module type X = sig
  type choice
  type t
  (* some methods we don't care about in this instance, like
     val modifySomething : t -> t *)
end

module Xbase = struct
  type choice = Smth | SmthElse
end

module X1 = (
struct
  include Xbase
  type t = { foo : int; bar : int }
end : X)

module X2 = (
struct
  include Xbase
  type t = { foo : int; star : int }
end : X)

module XStatic =
struct
  (* construct either an X1.t or X2.t from the string *)
  let read : string -> 'a =
    function
    | "X1" -> { X1.foo = 0, bar = 0 }
    | "X2" -> { X2.foo = 1, star = 1 }
end

しかし、これError: Unbound record field label X1.fooread関数で失敗します。私はそれを使用するなど、それを配置するさまざまな方法を試しましlet open X1 in { foo = 0, ... }たが、役に立たなかった。

これに対する私のアプローチは根本的に間違っていますか(つまり、これはモジュールでは不可能/非実用的であるため、クラスを使用する必要があります)、それとも些細なことを見逃しているだけですか?

編集:私が解決しようとしている問題を明確にし、それをと区別するために名前を変更module Xしました。module XBasemodule type X

4

3 に答える 3

6

最も簡単な方法は、sum 型を使用することです (免責事項: コードをコンパイルしようとしませんでした)。

module X1 = struct
  type t = { foo : int; bar : string }
  let modify_foo = ...
end
module X2 = struct
  type t = { foo : int; star : bool }
  let modify_foo = ...
end
type x1_or_x2 =
  | Left of X1.t
  | Right of X2.t

let read = function
  | "X1" -> Left { X1.foo = 1; bar = "bar" }
  | "X2" -> Right { X2.foo = 1; star = true }

let modify_foo = function
  | Left x1 -> Left (X1.modify_foo x1)
  | Right x2 -> Right (X2.modify_foo x2)

その事実を利用して共通の構造X1.tを共有したい場合は、型を因数分解できます。X2.tアイデアは、それらがそれぞれ製品タイプと同型であるということcommon_part * specific_to_x1ですcommon_part * specific_to_x2。したがって、x1_or_x2型は であり(common * specific_to_x1) + (common * specific_to_x2)、これは と同等common * (specific_to_x1 + specific_to_x2)です。

type common = { foo : int }
let modify_foo_common : common -> common = ...

type specific_x1 = { bar : string }
type specific_x2 = { star : bool }

type x1_or_x2 = common * specific_x1_or_x2
and specific_x1_or_x2 =
  | Left of X1.t
  | Right of X2.t

let read = function
  | "X1" -> { foo = 1 }, Left { bar = "bar" }
  | "X2" -> { foo = 1 }, Right { star = true }

let modify_foo (common, specific) = (modify_foo_common common, specific)

このように、共通部分に作用する定義は重複せず、一度宣言することができます。

PS: また、この非常に関連する質問も参照してください。これには、興味があり、良い答えがあります (レンズ!): Ptival: 間接的な手間をかけずにレコードっぽいデータ型を静的に「拡張」します

于 2012-12-10T14:06:01.207 に答える
1

このエラーは、とにモジュール署名があり、タイプのみ(タイプではない)Unbound record field label X1.fooであるという事実が原因です。つまり、構文は、署名の一部ではないすべての値とタイプを非表示にします。X1X2Xbasechoicet: XX1Xbase

しかし、このエラーを修正しても、もっと重要なことが表示されます。関数の戻り値のタイプは何readですか?X1.tとの両方にすることはできませんX2.t

于 2012-12-10T10:05:34.463 に答える
1

X1モジュールを強制的にX2モジュール typeXにすると、これらのモジュールの外にあるときに内部の型がどのように構造化されるかについての情報が失われます。コードの残りの部分 (つまり、モジュールのコンテンツ以外のすべて) は、何から作られているのかを知ることができませんX1.tX2.tこれらの型は、それぞれのモジュールを除いて他のすべてに対して抽象的になります。

問題の OOP アプローチは、おそらくデフォルトのコンストラクターを作成し、必要に応じて呼び出すことです。あなたの場合、モジュールでデフォルト値を定義し、インターフェース(モジュールタイプ)に宣言を追加して、外部コードがこれらのタイプの値を作成できるようにすることを意味します。

module type X = sig
    type t
    val default : unit -> t
    (* etc. *)
end;;

module X1 : X = struct
    include XBase     
    type t = {foo : int; bar : int}
    (* here I can change fields adlib *)
    let default () = {foo = 0; bar = 1}
   (* ... *)
end;;
(* here I don't have access to X1.t fields anymore *)

型が変更可能なフィールドや参照を保持していない場合は、関数ではなく ,defaultの単純な値を作成できますが、他のモジュールでそのようなフィールドを持つ可能性を維持したい場合があります。t

ここでの主なポイントは、抽象化された型に対して、型を公開しておくか、値を構築して操作する方法を提供する必要があるということです (後者は、コードにある程度実装されていると思います)。

モジュールの type を変更しない、または変更できない場合はX、mli ファイルを使用して、X1andを含むモジュール以外の他のモジュールの強制を行うことで、上位レベルで署名を強制することができますX2(つまり、コードがそのようなものに従っている場合)。デザイン)。

tさらに別の方法は、強制を行うときに関する型情報を追加することです。たとえばX1、型がchoice明示された の型の宣言を次に示します (既に に存在する場合X)。

module X1 : X with type choice = XBase.choice =
struct
   (* body of the X1 module *)
end;;
(* now I can access X1.choice constants from here, if XBase.choice is visible too *)

その type で同様のことを行うことができt、外部コードからそのコンテンツにアクセスできます。設計の観点からは、 type の値を操作するモジュールで既に演算子を提供している場合、明らかに問題を処理する最善の方法ではありませんt

于 2012-12-10T15:21:50.113 に答える