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