この記事では、.NETに組み込まれている標準的な型をF#がどう扱うかを簡単に見ていきます。

リテラル

F# では、C# と同じ構文を使用してリテラルを表しますが、いくつかの例外があります。

組み込み型は以下のように分類できます。

  • その他の型 (bool, char など)
  • 文字列型
  • 整数型 ( intuintbyte など)
  • 浮動小数点型 ( floatdecimal など)
  • ポインタ型 ( 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

results matching ""

    No results matching ""