2016-02-03 15 views
9

パラメトリックタイプのモジュールに応じて関数を記述する方法を理解しようとしていますが、どこでも同様のものは見つかりません。私は可能な限り問題を減らそうとし、このダミーの例で終わった。パラメトリックローカル抽象タイプ

module type Mappable = sig 
    type 'a t 
    val map : ('a -> 'b) -> 'a t -> 'b t 
end 

let plus (type m) (module M : Mappable with type 'a t = 'a m) (xs : int m) = 
    M.map (fun x -> x + 1) xs 

これはエラーError: Syntax error: module-expr expectedを生成します。

私が'aを削除すると、次のエラーが発生します。

Error: In this `with' constraint, the new definition of t 
     does not match its original definition in the constrained signature: 
     Type declarations do not match: type t is not included in type 'a t 
     They have different arities. 

これを行う正しい構文は何ですか?

答えて

7

OCaml 4.02.3では、ここであなたがしたいことは不可能だと思います。型変数なしで簡易版を見てみましょう:

module type Mappable = sig 
    type t 
    val map : ('a -> 'b) -> t -> t 
end 

let plus (type m) (module M : Mappable with type t = m) (xs : m) = 
    M.map (fun x -> x + 1) xs 

を上記型付け可能であるとplusは次のタイプがあります。その定義で

val plus : (module Mappable with type t = 'm) -> 'm -> 'm 

タイプmは、変数'mに抽象化されています。

元のコードに戻って、タイプplusが持つはずのものを考えてください。あなたは(type m)によって抽象mしようとしているので、それは次のようになります。

val plus : (module Mappable with type 'a t = 'a 'm) -> 'a 'm -> 'a 'm 

残念ながら、OCamlはタイプ'a 'mのこのフォームを可能に 高いkinded多型をサポートしていません。それは、ファーストクラスのモジュールの入力は、それを導入しないよう慎重に実装されているようです。

OCamlの高等種多型の現在の(残念な)状態を説明する以下の短い論文を見ることができます。これは、回避策を説明します:明示的なcoersionsのコストで、現在のOCamlの枠組みの中で、それをエンコードする方法:

https://ocamllabs.github.io/higher/lightweight-higher-kinded-polymorphism.pdf

自分で試したことがありませんが、同じ回避策はあなたの例にも適用することができます。

6

型制約は、通常のモジュールタイプの制約ではなく、特別なsyntactic construct、ポリモーフィック型を許可しないこととしてそれは、OCamlのは不可能である。

パッケージ型構文クラスはで登場(モジュールパッケージタイプ)タイプの式と注釈付きフォームのモジュールタイプのサブセットを表します。このサブセットは、限定されたフォームのオプションの制約を持つ名前付きモジュールタイプで構成されています。パラメータ化されていないタイプのみを指定できます。

通常の回避策は具体的なタイプに、すべての型変数をバインドするモジュールを作成することです:あなたの特定の例では

module type Mapper = sig 
    type a 
    type b 
    type src 
    type dst 
    val map : (a -> b) -> src -> dst 
end 

let plus (type src) (type dst) 
    (module M : Mapper with type dst = dst 
         and type src = src 
         and type a = int 
         and type b = int) (xs : src) : dst = 
    M.map (fun x -> x + 1) xs 

を彼らがしているので、'a'bをバインドする必要はなく、本質的に使用されないので、簡略化することができます:

module type Mapper = sig 
    type src 
    type dst 
    val map : ('a -> 'b) -> src -> dst 
end 

let plus (type src) (type dst) 
    (module M : Mapper with type dst = dst 
         and type src = src) (xs : src) : dst = 
    M.map (fun x -> x + 1) xs 

もちろん、これは非常に制限されていますが、これは今日まで可能です。

2

あなたが関数にモジュールを渡したい場合は、代わりにファンクタを使用する必要があります。

module F (M : Mappable) = struct 
    let plus xs = M.map (fun x -> x + 1) xs 
end