以下に基づいています。
F# レコードの奇妙な動作:
F# は構造型付けを使用しないことを理解することが重要です (つまり、フィールドが少ないレコードを受け取る関数の引数として、フィールドが多いレコードを使用することはできません)。これは便利な機能かもしれませんが、.NET 型システムにはうまく適合しません。これは基本的に、あまりにも凝ったものを期待してはならないことを意味します。引数は、よく知られた名前付きレコード タイプのレコードでなければなりません。
ニコラス:
Large fn のどの部分が使用されるかを事前に本当に知らない場合は、すべてを指定する必要があります。これは意味的に正しい唯一の選択です。
私は2つの可能な解決策を思いつきました:
解決策 1
基本的にユーザー関数を 2 つに分割します。良いことは、reducedPostprocessFn
必要なものだけを受け入れることです。したがって、この関数は簡単に推論して単体テストを行うことができます。postprocessFn
は非常に短いため、何をするかを簡単に確認できます。このソリューションは、によって提示されるアクティブなパターンに似ていると思いますPhillip Trelford
。アクティブパターンの利点は何ですか?
(* simulation *)
type Large = {v1: int; v2: int}
let simulation postprocessFn =
let large = {v1 = 1; v2 = 2}
postprocessFn large
(* user *)
let reducedPostprocessFn (v1: int) =
printfn "%d" v1
let postprocessFn (large: Large) =
reducedPostprocessFn large.v1
simulation postprocessFn
解決策 2
このソリューションではダックタイピングを使用しますが、次のことを行います。
http://msdn.microsoft.com/en-us/library/dd233203.aspx
明示的なメンバー制約: ... 一般的な使用を意図していません。
F# とダックタイピング
これはレコードタイプでは機能しないことを思い出しました。with member .... を使用してメンバーで修正できますが、技術的にはそれらのメンバーはフィールドです。
そこで、レコードの代わりに通常のクラスを使用しました。今では 2 つではなく 1 つの関数しか使用していませんが、正直なところ、F# でのダック タイピングは見苦しいものです。
(* simulation *)
type Large(v1, v2) =
member o.v1 = v1
member o.v2 = v2
let simulation postprocessFn =
let large = Large(1, 2)
postprocessFn large
(* user 2 *)
let inline postprocessFn small =
let v = (^a: (member v1: int) small)
printfn "%d" v
simulation postprocessFn