Equality 制約
の実装のためのメモ(宣伝)。
型制約は F# における型安全をマスターするためには必須の知識であるため、細かい仕様も見落としてはならない。
型制約は F# 言語仕様書 4.0 の 5.2 Type Constraints に記述されている。
Equality制約
Equality制約は滅多に使用しないが、かなり緩い制約になっている。
標準ライブラリでは(=)
、(<>)
、hash
にEquality制約がついている。
定義
こう書く。'a
にEquality 制約を指定できる。
<'a when 'a : equality>
挙動
Equality制約はほとんどの型が満たす。
なぜならEquals
メソッドのデフォルトであるSystem.Object.Equals
がリファレンスを比較するようになっているため、
全てのオブジェクトはEqualisy制約を満たす事になっている。
このコードで確認できる。
type A() = class end let test<'a when 'a : equality> (_: 'a) = () do test(A())
A
はIEquatable
を実装しないため、Equality制約を満たさないと予想するかもしれない。
実際には前述のとおり、Equality制約を満たす。
Equality制約を違反する型を定義するには、NoEqualityAttirbute
を型に付ける。
[<NoEquality; NoComparison>] type B() = class end do test(B()) // 型 'B' は 'NoEquality' 属性があるため、'equality' 制約をサポートしません
NoEqualityAttribute
、CustomEqualityAttribute
はComparison制約系の属性NoComparisonAttribute
、CustomComparisonAttribute
と組み合わせて使用する。
CustomEqualityAttribute
を指定した場合はObject.Equals(obj)
、IEquatable<_>
、IStructualEquatable
のどれかを実装しなければならない。
Equality制約を満たす条件は次の通り。1、2は言語仕様書に定義されている。3、4は実装を読んだ。
NoEqualityAttribute
を持っていない型- 依存する型が全てEquality制約を満たす型
- レコード、判別共用体、構造体の型パラメータ、フィールドに使用される型すべて(クラスは対象外)
System.Tuple
の要素の型- ジェネリック型の
EqualityConditionalOnAttribute
が指定されたパラメータ
- 関数
FSharpFunc
ではない型(つまりFunc
、Action
はEquality制約を満たす) CustomEqualityAttribute
が指定された型
C# で定義された型や、.Net標準ライブラリの型はEquality制約を満たすと言える。
わかりにくい項目の補足をする。
ジェネリック型のEqualityConditionalOnAttribute
が指定されたパラメータ
クラスの型パラメータにEquality制約を満たさない型を指定しても、Equality制約を満たすと判断される。
type C<'a>() = class end do test (C<int -> int>())
型パラメータにEqualityConditionalOnAttribute
を付けると、Equality制約の解決に使用されるようになる。
type C'<[<EqualityConditionalOn>]'a>() = class end do test (C'<int -> int>()) // 型 '(int -> int)' は関数型なので、'equality' 制約をサポートしませ
CustomEqualityAttribute
が指定された型
レコード、判別共用体、構造体に使う。依存する型がEquality制約を満たさなくても良くなる。
[<CustomEquality; NoComparison>] type D<'a> = { Field: 'a } with override this.Equals(_) = true override this.GetHashCode() = 0 let f = fun () -> () do test({ Field = f })
EqualityConditionalOnAttribute
と同時に使われた場合は、EqualityConditionalOnAttribute
も考慮する必要がある。
次のような型を定義した場合は、
[<CustomEquality; NoComparison>] type E<[<EqualityConditionalOn>]'a, 'b> = A of 'a | B of 'b with override this.Equals(_) = true override this.GetHashCode() = 0
E<int, int -> int>
という型はEquality制約を満たすが、E<int -> int, int -> int>
は'a
が関数であるためEquality制約を満たさない。
まとめ
実装するのがツライ
リンク
Equality and Comparison Constraints in F# | Don Syme's WebLog on F# and Related Topics