2013-04-29 8 views
8

F#は実存型をサポートしていません。だから私は自分の考えを表現する別の方法を探しています。F#で存在型を表現する

私はデータ構造を持っており、その内容はさまざまな方法で解釈できます。この特定の例では、私はそれがintまたは実数とみなすことができると仮定します:realが何か意味がある

type Packed = (* something sensible *) unit 

type PackedType = | PackedInt 
        | PackedReal 


let undefined<'a> : 'a = failwith "undefined" 

let unpackInt : Packed -> int = undefined 
let unpackReal : Packed -> real = undefined 

let packInt : int -> Packed = undefined 
let packReal : real -> Packed = undefined 

は、言う:

type real = int * int 

let addReal : real -> real -> real = undefined 

は今、私は機能addPacked : PackedType -> Packed -> Packed -> Packedを必要としています。私はそれが一般的になりたい、それは次のようになります。

type NumberOp = [forall t] { opPack : 't -> Packed; opUnpack : Packed -> 't; opAdd : 't -> 't -> 't } 

let getNumberOp (t : PackedType) = 
    match t with 
    | PackedInt -> { opPack = packInt; opUnpack = unpackInt; opAdd = (+) } 
    | PackedReal -> { opPack = packReal; opUnpack = unpackReal; opAdd = addReal } 


let addPacked (t : PackedType) (a : Packed) (b : Packed) = 
    let { opPack = pack; opUnpack = unpack; opAdd = add } = getNumberOp t 
    pack <| add (unpack a) (unpack b) 

は、ここで私は NumberOpがexistetialことになってしまいました。だから私はこれを表現する別の方法があるかどうか尋ねています。 Packed[un]pack*の機能と addPackedのタイプを変更することはできません。

私はthis答えを見つけました。それは "よく知られているパターン"があると述べていますが、テキストは読みにくく、動作させることができませんでした。一般的に

+1

も参照してください。http://stackoverflow.com/questions/15690053/impredicative-polymorphism-in-f –

答えて

12

∀x.(∀t.F<t> → x) → x 

残念ながら、各ユニバーサル定量化は、F#で新しいタイプを作成する必要が

∃t.F<t> 

としてあなたはタイプをエンコードすることができますが、その忠実なエンコーディングは、に加えて、二つのタイプが必要ですF.あなたの例でこれを行う方法は次のとおりです。

type 't NumberOps = { 
    opPack : 't -> Packed 
    opUnpack : Packed -> 't 
    opAdd : 't -> 't -> 't 
} 

type ApplyNumberOps<'x> = 
    abstract Apply : 't NumberOps -> 'x 

// ∃ 't. 't NumberOps 
type ExNumberOps = 
    abstract Apply : ApplyNumberOps<'x> -> 'x 

// take any 't NumberOps to an ExNumberOps 
// in some sense this is the only "proper" way to create an instance of ExNumberOps 
let wrap n = { new ExNumberOps with member __.Apply(f) = f.Apply(n) } 

let getNumberOps (t : PackedType) = 
    match t with 
    | PackedInt -> wrap { opPack = packInt; opUnpack = unpackInt; opAdd = (+) } 
    | PackedReal -> wrap { opPack = packReal; opUnpack = unpackReal; opAdd = addReal } 

let addPacked (t : PackedType) (a : Packed) (b : Packed) = 
    (getNumberOps t).Apply 
     { new ApplyNumberOps<_> with 
      member __.Apply({ opPack = pack; opUnpack = unpack; opAdd = add }) = 
       pack <| add (unpack a) (unpack b) } 

boiがたくさんあります残念ながら、ここにlerplate。また、各ヘルパータイプの抽象メンバには役に立たない名前のApplyを使用しました。あなたが好む名前を見つけることができれば、より意味のあるものに置き換えることもできます。私は自分のスタイルにかなり近づけようとしましたが、私のコードでは、addPackedの範囲内でレコードを破壊することは避けたいのではなく、フィールドアクセサを直接使用します。

+0

ありがとうございました。しかし、私はこれを理解しようと数時間を費やしてきましたが、コードがどのように動作するかはまだ分かりません。さらに悪いことに、推測された型を見れば、もう少し明確になることが期待されますが、コードはコンパイルに失敗します。コンパイラはあなたの '新しいApplyNumberOps <_>'の 'Apply'はタイプが間違っているので、抽象メソッドをオーバーライドしないと言います。 – kirelagin

+0

@kirelagin - 'Packed'の定義として' unit'以外のものを使用するとコンパイルされます.'uns'は.NET interopのために少し特別なものであり、ジェネリックでうまくいくとは限りません。 – kvb

+0

素晴らしい。ありがとうございました! – kirelagin

関連する問題