6

私のコードには、と呼ばれる基本的な読み取り/書き込み操作を提供するデータベースアクセスコンテキストCouchDB.ctxがあります。次に、アプリケーションのさまざまなモジュールが、などの追加機能でそのクラスを拡張しますAsync.ctx

Cacheモジュールをラップしたモジュールを実装していSourceます。モジュール関数はCacheコンテキスト引数を取り、データベースを操作します。その後、一部の呼び出しはSourceコンテキストとともにモジュールに転送されます。

私はこれに沿ってファンクターを定義する必要があります:

module CouchDB = struct
  class ctx = object
    method get : string -> string option monad 
    method put : string -> string -> unit monad
  end
end

module AsyncDB = struct
  class ctx = object
    inherit CouchDB.ctx
    method delay : 'a. float -> (ctx -> 'a monad) -> 'a monad 
  end
end

module type SOURCE = sig
  class ctx = #CouchDB.ctx (* <-- incorrect *)
  type source
  val get : source -> ctx -> string monad
end

module Cache = functor(S:SOURCE) -> struct
  class ctx = S.ctx
  type source = S.source
  let get source ctx = 
    bind (ctx # get source) (function 
     | Some cache -> return cache
     | None -> 
       bind (S.get source ctx) 
         (fun data -> bind (ctx # put source data) 
                        (fun () -> return data)) 
end

module SomeSource = struct
  class ctx = AsyncDB.ctx
  type source = string
  let get s ctx = 
    ctx # async 300 (some_long_computation s)
end

module SomeCache = Cache(SomeSource)

Source問題は、モジュールで使用されるコンテキストがのサブタイプである必要があるという事実を表現できないことですCouchDB.ctx。上記のコードはエラーを返します:

A type variable is unbound in this type declaration.
In type #CouchDB.ctx as 'a the variable 'a is unbound

このタイプの制約をどのように表現しますか?

4

2 に答える 2

5

[廃止...

最も近い方法は、署名を次のように定義することです。

module type SOURCE = sig
  type 'a ctx = 'a constraint 'a = #CouchDB.ctx
  type source
  val get : source -> 'a ctx -> string 
end

しかしもちろん、次のように書くこともできます:

module type SOURCE = sig
  type source
  val get : source -> #CouchDB.ctx -> string 
end

編集: OCaml はオブジェクトに構造型付けを使用することに注意してください。つまり、必要に応じて、上記よりも制限を厳しくすることはできません。get引数をインスタンスまたは派生クラスに制限することさえありませんCouchDB.ctx。(少なくとも) 同じメソッドを持つオブジェクトはすべて互換性があります。書くときも

  val get : source -> CouchDB.ctx -> string 

同じメソッドを持つ任意のオブジェクトを渡すことができます。タイプCouchDB.ctxは、同じ名前のクラスによって生成されたオブジェクトとたまたま一致する特定の構造オブジェクト タイプの略語です。それらに限定されるものではありません。念のために言っておきますが、これは機能と見なされます。

======]

編集 2: 拡張された例で、あなたが望むものとその理由がわかりました。残念ながら、それは OCaml で直接行うことはできません。部分的に抽象型が必要になります。つまり、書くことができる必要があります

module type SOURCE = sig
  type ctx < CouchDB.ctx
  ...
end

これは OCaml では利用できません。ただし、署名で明示的なアップキャストを提供する意思がある場合は、近づくことができます。

module type SOURCE = sig
  type ctx
  val up : ctx -> CouchDB.ctx
  type source = string
  val get : source -> ctx -> string monad
end

次に、 ではCache、 の出現を に置き換え、同様に を置き換える必要がctx#getあり(S.up ctx)#getますctx#put

module Cache = functor (S:SOURCE) -> struct
  type ctx = S.ctx
  type source = S.source
  let get source ctx = 
     bind ((S.up ctx)#get source) ...
end

module SomeSource = struct
  type ctx = AsyncDB.ctx
  let up ctx = (ctx : ctx :> CouchDB.ctx)
  type source = string
  let get s ctx = ...
end

module SomeCache = Cache (SomeSource)

type source = string署名も透明にしたことに注意してくださいSOURCE。それがなければ、ファンクターでどのようctx#get sourceにタイプチェックできるかわかりません。Cache

于 2012-05-14T18:04:56.540 に答える
1

あなたが何を求めているのか私が誤解していない限り、これでうまくいくはずです:

module type SOURCE = sig
  class ctx : CouchDB.ctx
  type source
  val get : source -> ctx -> string
end

class ctx : CouchDB.ctxクラス仕様です。OCamlのドキュメントでは、それらを次のように説明しています

これは、クラス定義の署名の対応物です。クラス仕様は、同じ型パラメーターを持ち、それらの型が一致する場合、クラス定義と一致します。

こんなのもあります

module type SOURCE = sig
  class type ctx = CouchDB.ctx
  type source
  val get : source -> ctx -> string
end

これは微妙に異なります。前者はモジュール内に実際のクラス定義を必要とし、後者はクラス定義またはクラス型定義 (つまり、クラス型エイリアス) を受け入れます。

于 2012-05-14T18:45:48.360 に答える