Null許容参照型がF#にも来るぞー
Null許容参照型
C# 8.0で導入された、Null許容参照型(Nullable Reference Types)がF#にも来るらしいです。
従来のNull対応
F#では従来からnull安全のための設計がされていました。基本的にnullを許さない方針で、代わりに用意されたOption型やValueOption型を使うように設計されています。どうしてもnullを扱う場合は、[<AllowNullLiteral>]属性を付けて明示するようになっていました。
しかし、F#がどんなに努力しても.NETクラスライブラリからはnullが送られてくるという現実からは避けられませんでした。
C# 8.0のNull許容参照型
C# / .NET本体が重い腰を上げ、Null許容参照型への対応をしました。これにより、.NETクラスライブラリからnullが送られないように、送られる場合は判別できるように改良されました。
C#ではコンパイラが制御フローを把握し、null値追跡をします。具体的にはnullチェックをするとその変数はnullでないと扱われるように切り替わります。
F# 9.0のNull許容参照型
これを受けて、F#でもNull許容参照型が再設計されました。従来より非nullが前提にあるため、C#のような変数が途中で扱いが変わるようなことはありません。
なお特徴を従来機能と比較しておくと
-
Option- インライン展開できないとGCが発生し、コストがかかる
-
Some nullでnull値を保持できる
-
ValueOption- GCは発生しないがオーバーヘッドがある
-
ValueSome nullでnull値を保持できる
- Null許容参照型
- コンパイル時に完結し、実行時にはオーバーヘッドがない
-
null値を保持できない
F#でのNull許容参照型の書き方
型の書き方
| C# | F# |
|---|---|
string? |
string | null |
string! |
string |
string |
string |
元々F#ではnullを認めていないので、C#におけるstring!が既定の動作です。
追加ライブラリ
これらのライブラリ関数を使うことで、Null許容参照型を扱うことになります。
val inline isNull: value:'T -> bool when 'T : not struct and 'T : null
従来からありましたが、nullかどうか判別します。boolで返されるので使い勝手が良くありません。
val inline nonNull : value: 'T | null -> 'T when 'T : not null and 'T : not struct
nullなら例外を出し、nullでないことを保証します。
val inline withNull : value:'T -> 'T | null when 'T : not null and 'T : not struct
Null許容参照型へ変換します。
val inline (|Null|NonNull|) : value: 'T | null -> Choice<unit, 'T> when 'T : not null and 'T : not struct
nullか判別するアクティブパターンです。
val inline (|NonNullQuick|) : value: 'T | null -> 'T when 'T : not null and 'T : not struct
nonNullと同様にnullでないことを保証します。
val inline defaultIfNull : defaultValue:'T -> arg:'T | null -> 'T when 'T : not null and 'T : not struct
nullの場合はデフォルト値を返すことでnullでないことを保証します。
val inline nullArgCheck : argumentName:string -> 'T | null -> 'T when 'T : not null and 'T : not struct
引数チェックで使用します。nullならArgumentNullExceptionを出し、nullでないことを保証します。
Optionとの比較
OptionやValueOptionと比較した方が分かりやすいかな?
Option |
Null許容参照型 |
|---|---|
Some |
withNull / NonNull
|
None |
null / Null
|
defaultArg |
defaultIfNull |
Option.get |
nonNull |
Option.isNone |
isNull |
Option.toObj |
withNull |
Discussion