この記事では、.NETに組み込まれている標準的な型をF#がどう扱うかを簡単に見ていきます。
リテラル
F# では、C# と同じ構文を使用してリテラルを表しますが、いくつかの例外があります。
組み込み型は以下のように分類できます。
- その他の型 (
bool
,char
など) - 文字列型
- 整数型 (
int
、uint
、byte
など) - 浮動小数点型 (
float
、decimal
など) - ポインタ型 (
IntPtr
など)
以下の表は、プリミティブ型とそのF#キーワード、接尾辞(ある場合)、例、対応する.NET CLR型を示しています。
その他の型
オブジェクト | ユニット | 真偽値 | 文字 (Unicode) |
文字 (ASCII) |
|
---|---|---|---|---|---|
キーワード | obj | unit | bool | char | byte |
接尾辞 | B | ||||
例 | let o = obj() | let u = () | true false | 'a' | 'a'B |
.NET型 | Object | (相当なし) | Boolean | Char | Byte |
オブジェクトとユニットは厳密には.NETのプリミティブ型ではありませんが、網羅性のために含めました。
文字列型
文字列 (Unicode) |
逐語的文字列 (Unicode) |
三重引用符文字列 (Unicode) |
文字列 (ASCII) |
|
---|---|---|---|---|
キーワード | string | string | string | byte[] |
接尾辞 | ||||
例 | "first\nsecond line" | @"C:\name" | """can "contain"" special chars""" | "aaa"B |
.NET型 | String | String | String | Byte[] |
通常の文字列内では、 \n
、 \t
、 \\
などの特殊文字を使えます。引用符はバックスラッシュでエスケープする必要があります。 \'
と \"
がその例です。
逐語的文字列では、バックスラッシュは無視されます。これはWindowsのファイル名や正規表現パターンに便利です。ただし、引用符は2つ重ねる必要があります。
三重引用符文字列は、VS2012で新しく導入されました。特殊文字をエスケープする必要がないため、埋め込み引用符を簡単に扱えます。XMLに最適です。
整数型
8ビット (符号付き) |
8ビット (符号なし) |
16ビット (符号付き) |
16ビット (符号なし) |
32ビット (符号付き) |
32ビット (符号なし) |
64ビット (符号付き) |
64ビット (符号なし) |
任意 精度 |
|
---|---|---|---|---|---|---|---|---|---|
キーワード | sbyte | byte | int16 | uint16 | int | uint32 | int64 | uint64 | bigint |
接尾辞 | y | uy | s | us | u | L | UL | I | |
例 | 99y | 99uy | 99s | 99us | 99 | 99u | 99L | 99UL | 99I |
.NET型 | SByte | Byte | Int16 | UInt16 | Int32 | UInt32 | Int64 | UInt64 | BigInteger |
BigInteger
は、すべてのバージョンのF#で使えます。.NET 4以降では、.NETの基本ライブラリの一部として含まれています。
整数型は16進数や8進数でも表せます。
- 16進数の接頭辞は
0x
です。たとえば、0xFF
は16進数で255を表します。 - 8進数の接頭辞は
0o
です。たとえば、0o377
は8進数で255を表します。
浮動小数点型
32ビット 浮動小数点 |
64ビット(デフォルト) 浮動小数点 |
高精度 浮動小数点 |
|
---|---|---|---|
キーワード | float32, single | float, double | decimal |
接尾辞 | f | m | |
例 | 123.456f | 123.456 | 123.456m |
.NET型 | Single | Double | Decimal |
F#ではデフォルトで float
を使いますが、 double
も使えることに注意してください。
ポインタ型
ポインタ/ハンドル (符号付き) |
ポインタ/ハンドル (符号なし) |
|
---|---|---|
キーワード | nativeint | unativeint |
接尾辞 | n | un |
例 | 0xFFFFFFFFn | 0xFFFFFFFFun |
.NET型 | IntPtr | UIntPtr |
組み込みプリミティブ型間のキャスト
注:このセクションではプリミティブ型のキャストのみを扱います。クラス間のキャストについては、オブジェクト指向プログラミングのシリーズを参照してください。
F#には直接的な「キャスト」構文はありませんが、型間のキャストを行うヘルパー関数があります。これらのヘルパー関数は型と同じ名前を持ちます。 Microsoft.FSharp.Core
名前空間で見つけることができます。
たとえば、C#では次のように書きます。
var x = (int)1.23
var y = (double)1
F#では次のように書きます。
let x = int 1.23
let y = float 1
F#では、数値型に対するキャスト関数しかありません。特に、 bool
に対するキャストはなく、 Convert
などを使う必要があります。
let x = bool 1 // エラー
let y = System.Convert.ToBoolean(1) // OK
ボクシングとアンボクシング
C#や他の.NET言語と同様に、プリミティブなintやfloat型は値型であり、クラスではありません。 通常はあまり意識しませんが、特定の状況では問題になることがあります。
まず、単純な例を見てみましょう。以下の例では、 Object
型のパラメータを受け取り、そのまま返す関数を定義します。
int
を渡すと、暗黙的にオブジェクトにボクシングされます。テストコードから分かるように、結果は int
ではなく object
を返します。
// Object型のパラメータを持つ関数を作る
let objFunction (o:obj) = o
// テスト:整数を渡して呼び出す
let result = objFunction 1
// 結果は
// val result : obj = 1
result
が整数ではなくオブジェクトであることは、注意しないと型エラーの原因になることがあります。たとえば、結果を元の値と直接比較することはできません。
let resultIsOne = (result = 1)
// error FS0001: この式に必要な型は 'obj' ですが、
// ここでは次の型が指定されています 'int'
この状況や似たような状況に対処するため、 box
キーワードを使ってプリミティブ型を直接オブジェクトに変換できます。
let o = box 1
// 上の比較例を再テストし、ボクシングを使う
let result = objFunction 1
let resultIsOne = (result = box 1) // OK
オブジェクトをプリミティブ型に戻すには、 unbox
キーワードを使います。ただし、 box
とは異なり、アンボクシング先の型を指定するか、コンパイラが正確に型推論できるよう十分な情報が必要となります。
// intをボクシング
let o = box 1
// 対象の値の型が分かっている場合
let i:int = unbox o // OK
// unboxで明示的に型を指定
let j = unbox<int> o // OK
// 型推論が可能なので、型注釈は不要
let k = 1 + unbox o // OK
したがって、先ほどの比較の例も unbox
を使って行えます。 int
型との比較なので、明示的な型注釈は必要ありません。
let result = objFunction 1
let resultIsOne = (unbox result = 1) // OK
しかし、十分な型情報を指定しないと、悪名高き「値制限」エラーに遭遇します。
let o = box 1
// 型が指定されていない
let i = unbox o // error FS0030: 値の制限
解決方法は、型推論を助けるようにコードを並べ替えるか、どうしても駄目な場合は明示的な型注釈を追加することです。詳しくは型推論に関する記事のトラブルシューティングのまとめを参照してください。
型検出とボクシングの組み合わせ
パラメータの型に基づいてマッチングを行う関数を、 :?
演算子を使って作りたいとします。
let detectType v =
match v with
| :? int -> printfn "これは整数です"
| _ -> printfn "それ以外です"
残念ながら、このコードはコンパイルに失敗し、次のエラーが出ます。
// error FS0008: この型 'a から型 int へのランタイム型変換またはランタイム型テストには、
// このプログラムの場所の前方にある情報に基づく不確定の型が使用されています。
// 実行時の型テストは一部の型では許可されていません。さらなる型注釈が必要です。
「実行時の型テストは一部の型では許可されていません。」というメッセージが問題を示しています。
解決方法は、値を「ボクシング」して参照型に換し、その後で型チェックを行うことです。
let detectTypeBoxed v =
match box v with // "box v"を使う
| :? int -> printfn "これは整数です"
| _ -> printfn "それ以外です"
// テスト
detectTypeBoxed 1
detectTypeBoxed 3.14