5

次の DU があるとします。

type Something =
| A of int
| B of string * int

今、私は次のような関数でそれを使用します:

let UseSomething = function
| A(i) -> DoSomethingWithA i
| B(s, i) -> DoSomethingWithB s i

それは機能しますが、DU を DoSomethingWith* 関数に渡すために、DU を分解する必要がありました。DoSomethingWithA を次のように定義しようとするのは自然なことだと思います。

let DoSomethingWithA (a: Something.A) = ....

しかし、コンパイラは型 A が定義されていないと不平を言います。

引数を古い int だけでなく、Something.A に制限したいというのは、F# の哲学と完全に一致しているように思えます。

4

3 に答える 3

5

注意すべき重要なことは、Aとが同じBのコンストラクターであるということです。そのため、 とケースを別々に使用しようとすると、網羅的でないパターン マッチングの警告が表示されます。SomethingAB

IMO、DUのすべてのケースを分解することは良い考えです。タイプセーフであり、それらのケースを処理したくない場合でもそれらのケースを処理することを考える必要があるからです。同じ方法で DU を繰り返し分解する必要がある場合、問題が発生する可能性があります。その場合、 DUmapで およびfold関数を定義することをお勧めします。

let mapSomething fa fb = function
| A(i) -> fa i
| B(s, i) -> fb s i

DU のフォールドについては、@Brian による優れたCatamorphism シリーズを参照してください。

それはまた、あなたの例は問題ないと言いました。あなたが実際に処理するのはstringint解体後の価値です。

アクティブ パターンを使用して、2 つのケースを別々に使用できます。

let (|ACase|) = function A i -> i | B _ -> failwith "Unexpected pattern B _"
let (|BCase|) = function B(s, i) -> (s, i) | A _ -> failwith "Unexpected pattern A _"

let doSomethingWithA (ACase i) = ....

しかし、推論されたタイプはdoSomethingWithA同じままで、関数に渡すときに例外が発生しB _ます。したがって、IMOを行うのは間違っています。

于 2013-03-15T10:15:22.217 に答える
4

他の答えは正確です。F#では、型ABはなくコンストラクターです。これは、Haskell や ML ファミリーの他の言語などの厳密に型指定された関数型言語で採用されている従来のアプローチです。ただし、他のアプローチもあります。たとえば、Scala では、AB実際には のサブクラスになるとSomething思います。そのため、必要に応じて、より具体的な型を使用できます。設計の決定にどのようなトレードオフが関係しているのかは完全にはわかりませんが、一般的に言えば、継承によって型推論が困難/不可能になります (そして、Scala でのステレオタイプの型推論に忠実であることは、Haskell や ML 言語よりもはるかに悪いことです)。

于 2013-03-15T15:20:41.570 に答える
3

Aは型ではなく、単なるコンストラクタですSomething。パターン マッチングを回避する方法はありませんが、これは必ずしも悪いことではありません。

そうは言っても、F# はアクティブ パターンと呼ばれるものを提供します。

let (|AA|) = function 
    | A i -> i 
    | B _ -> invalidArg "B" "B's not allowed!"

次のように使用できます。

let DoSomethingWithA (AA i) = i + 1

しかし、あなたがそれをしたい本当の理由はありません! ボンネットの下で同じ古いパターン マッチングを行うことに加えて、実行時エラーが発生する可能性があります。

いずれにせよ、UseSomethingF# の実装は完全に自然です。

于 2013-03-15T10:16:15.743 に答える