2016-09-01 10 views
3

型の引数にレコード型であり、特定のプロパティが必要な汎用関数を作成しようとしています。特定のプロパティを持つレコード型のF#型制約

let foo<'a> (a : 'a) = 
    a' = { a with bar = "baz" } 
    a' 

私はThe record label bar is not definedを示すエラーを取得し、これをコンパイルする:ここでは、関連するコンパイラエラーが発生したサンプルです。

私は、次のタイプの制約追加しようとした

let foo<'a when 'a : (member Id : string)> = 
    // ... 

をそれは

This code is not sufficiently generic. The type variable ^a when ^a : (member get_Int : ^a -> string) could not be generalized because it would escape its scope.は私がこれを適切にやらせるだろう型制約を指定する方法があることを訴えて、どちらかのコンパイルされませんでした?

+2

まず、関数は静的に制約されるためにインラインでなければなりません。そのプロパティを持つレコードの特定のプロパティを読み取る関数を作成することはできますが、複製するには静的に制約された '' with''構文を使用できません。あなたはその時点で反射のようなものが必要です。 – Gustavo

+0

レンズを見るべきです。 –

+0

@FyodorSoikin私はレンズがこれを解決するとは思わない。多形レンズでさえない。前に述べたように、AFAIKはメンバ '' bar''の静的メンバ拘束を使って '' bar = ..''構造体を呼び出す方法はありません。 – Gustavo

答えて

3

スタティックメンバの制約を使用してこれを指定する方法はないと思います。スタティックメンバの制約はかなり制限されており、主に他の通常のテクニックに加えて利用できる抽象化メカニズムです。

私がこのような問題を解決しようとしていたのであれば、おそらくインターフェースを使うことを考えていました。これは必ずしも最良の方法ではありませんが、具体的な状況を知らなくても妥当なデフォルトアプローチです。

type ISetA<'T> = 
    abstract WithA : string -> 'T 

type MyRecord = 
    { A : string } 
    interface ISetA<MyRecord> with 
    member x.WithA(a) = { x with A = a } 

レコードを実装するとき、あなたは(あなたがあなただけの静的メンバ制約を使用してこの操作を行うことができればより少し多くの作業を行う必要があります)インターフェイスの実装を追加する必要があります。しかし、その後、あなたはまた、明示的に使用量が、静的な制約を使用した場合よりも簡単です

...これはタイプの使用目的であることを言っている:

let setA (setA:ISetA<_>) = 
    setA.WithA "Hello" 

setA { A = "Test" } 
4

私は最初のトーマスの答えを読んでお勧めします。可能であれば、静的に解決された型制約を使用するのは一般的に避けなければなりません。それらは.NETではなくF#コンパイラの機能ですので、コードの再利用性をある程度制限しています。つまり、コンパイル時には非常に強力で、有用な制約を課すことができます。

それらを使用するための構文もそれほど快適ではないですが、あなたが広まり残っている場合、あなたはこのような何か行うことができます:しかし、あなたが実際のレコードであることに種類を制限することはできませんことを

type Test = {Bar : string} 

let inline foo (a : ^a) = 
    "foo " + ((^a) : (member Bar : string) (a)) 

let foobar = foo {Bar = "bar"} // prints "foo bar" 

注意を単にstringのメンバーBarを持つものです。したがって、これも解決されます:

type Test2(str : string) = member this.Bar = str 

let foobar2 = foo (Test2("bar")) // prints "foo bar" 
2

あなたの目標は何か分かりません。

一般的なレコードのプロパティを読み込むのが目的の場合は、TheInnerLightの動作例を参照してください。

これで、多くのタイプのレコードをクローンする関数を作成する場合は、デザインを変更する必要があります。

もしそうなら、あなたはTomasが提案するアプローチに従うことができます。それに加えて、もう一つの選択肢があります:ネストされたジェネリックレコードを使用する。

最終的に、このデザインでは、FyodorSoikinが最初に示唆したようにレンズに移動することができます。

+0

私はこのアプローチが好きですが、試してみるとすぐに次のステップに遭遇しました:http://stackoverflow.com/questions/39306148/create-new-record-type-in​​line-by-extending-existing-record- with-new-member –

+0

いいえ、結果の型があらかじめ定義されていない限り、型を平坦化することはできません。現時点では、F#には匿名型はありません。 – Gustavo

関連する問題