2012-05-22 1 views
6

すべての構造が不変条件を保持できるが、パターンマッチングのために構造を破壊することができるモジュールメンバーを通過するように型を定義します。destructuringを許可しながら不変条件を保存する

私はOCamlのを勉強していますが、次はほとんど左は、このように

# let p = Range.make 1 2;; 
val p : Range.t = {Range.left = 1; Range.right = 2} 
# let q = Range.make 2 1;; 
Exception: Range.InvalidRange (2, 1). 

を働く権利

module Range : sig 
    type t = private { left:int; right:int } 
    exception InvalidRange of (int*int) 
    val make : int -> int -> t 
end = struct 
    type t = { left:int; right:int } 
    exception InvalidRange of (int*int) 
    let make left right = if left < right 
    then { left; right } 
    else raise (InvalidRange (left, right)) 
end 

より厳密に小さくなければならないという不変でint型のペアのために働くとファッションの後に破局が働く

# let {Range.left=x; Range.right=y} = p;; 
val x : int = 1 
val y : int = 2 

建設中に失敗する

# let badp = {Range.left = 2; Range.right = 1};; 
    let badp = {Range.left = 2; Range.right = 1};; 
Error: Cannot create values of the private type Range.t 
# open Range;; 
# let badp = {left = 2; right=1};; 
    let badp = {left = 2; right=1};; 
Error: Cannot create values of the private type Range.t 

しかし、私が実際にやってみたいのは、タプルを破壊するという構文上の便利さです。

module Range : sig 
    type t = private int*int 
    exception InvalidRange of (int*int) 
    val make : int -> int -> t 
end = struct 
    type t = int*int 
    exception InvalidRange of (int*int) 
    let make left right = if left < right 
    then (left, right) 
    else raise (InvalidRange (left, right)) 
end 

をその後私はタプルパターンを使用して、それをdestructureすることはできません: 以下は動作しません

# let r = Range.make 1 2 ;; 
val r : Range.t = (1, 2) 
# let (a, b) = r;; 
    let (a, b) = r;; 
Error: This expression has type Range.t 
     but an expression was expected of type 'a * 'b 

私はtype t = R of (int * int)に種類を変えることができるが、私は軽するこれらを必要とします可能な限りメモリに重みを付けます。何か案は?

答えて

9

としてはmanualに説明し、明示的な強制を必要とする:

# let (a, b) = (r :> int*int);; 
val a : int = 1 
val b : int = 2 
+0

マニュアルへのポインタありがとうございます。名義型と消去型の間の強制が自動的に起こるときとそうでないときとで頭を包むために私はしばらく時間がかかるだろう。型強制演算子の存在は、非構造化が常に可能であることを意味するので、関数によって閉じられた値を除いて、実際に情報が隠れることはありません(例えば、変数または秘密)。 –

+2

1)透過型、2)抽象型、3a)民間バリアントまたはレコード型3b)プライベート型の略語に違いがあります。 1)強制は両方向で可能であり暗黙的です。 2)では、強制は全くできません。 3a)については、一方向の強制が可能であり、暗黙のように見える。 3b)のために、強制は一方向で可能であり、明示的であるように思われる。変換には計算内容がない(実行時に消去される)ことに注意することが重要です。 – esope

+1

ありがとうございます。したがって、型システムは二重であり、メッセージのディスパッチに影響する具体的なタイプと抽象タイプの区別を持つ基本的なタイプと、ディスパッチを排除できますが影響を及ぼさない消去の対象です。 –

2

私はちょうどObjsize(レポートOCamlの値の大きさ)で、このテストを実行しました。あなたは(私は)これらの値を信じるなら

# type fancy = R of int * int;; 
type fancy = R of int * int 
# Objsize.objsize (R (3, 5));; 
- : Objsize.info = {Objsize.data = 2; Objsize.headers = 1; Objsize.depth = 0} 
# Objsize.objsize (3,5);; 
- : Objsize.info = {Objsize.data = 2; Objsize.headers = 1; Objsize.depth = 0} 

、あなた自身のシングルのコンストラクタタイプの代わりにタプルを使用するための何のサイズペナルティはありません。

+0

私はあなたがその質問に答えるとは思わない。 – Thomash

+3

私は「私は型をint型int型に変えることができましたが、これらをできるだけ軽量メモリにする必要がある」という部分に答えようとしていました。私の主張は、 'int * int'のRは重量級のメモリではないということです。 –

4

これを行う簡単な方法は、to_tuple機能を追加してツアータイプを抽象化することです。

module Range : sig 
    type t 
    exception InvalidRange of (int*int) 
    val make : int -> int -> t 
    val to_tuple : t -> (int * int) 
end = struct 
    type t = { left:int; right:int } 
    exception InvalidRange of (int*int) 

    let make left right = if left < right 
    then { left; right } 
    else raise (InvalidRange (left, right)) 

    let to_tuple t = t.left, t.right 

end 

、あなたは

let (a, b) = to_tuple range 
+2

あなたのソリューションは機能します。プライベート型を使用する場合との違いは、 'to_tuple'には計算コンテンツがあり、強制(_:> _)には計算コンテンツがありません。これを理解するには、 'Range.t list'型の値を考えて、'(int * int)list'型のリストに変換しようとしてください。あなたのソリューションでは 'List.map to_tuple _'(リストをコピーします)を使う必要があります。 private型の解法では、何もしない(変換は消去されます) '(_:>(int * int)list)'だけを書いています。 – esope

3

を行うことができますtype t = R of (int * int)のソリューションは、メモリの面で軽量になり、構文的に強制ソリューションよりも少し軽量です。 OCamlは、単一のコンストラクタデータ型の大文字小文字を最適化します。したがって、それを支払う必要はありません。 (私はこの主張の正式な参照はないが、Adam Chlipala(OCamlの専門家)はここにそれを言及している:http://adam.chlipala.net/cpdt/html/Subset.html)。

関連する問題