F#と列挙体

F#の解説記事やドキュメント等の作成においてAzure Notebooksは活躍しまくりなんじゃないか説を検証するために、簡単なノートを作成してみようと思います。今回のテーマは列挙体です。F#における列挙体は判別共用体の陰に隠れてしまいがちですが、実務などでF#を使用する際にはどうしても必要になってきます。

基本

In [1]:
// 列挙体の定義
type MyEnum =
    | One   = 1
    | Two   = 2
    | Three = 3
In [2]:
MyEnum.One
Out[2]:
One {value__ = 1;}
In [3]:
enum<MyEnum> 2
Out[3]:
Two {value__ = 2;}
In [4]:
enum<MyEnum> 4
Out[4]:
4 {value__ = 4;}
In [5]:
MyEnum() // コンストラクタ
Out[5]:
0 {value__ = 0;}

列挙体を利用する関数の例

int関数を利用することで、数値として扱うことができます。

In [6]:
let next : MyEnum -> MyEnum = int >> ( + ) 1 >> enum
In [7]:
next MyEnum.One
Out[7]:
Two {value__ = 2;}
In [8]:
next MyEnum.Three
Out[8]:
4 {value__ = 4;}

フラグとしての列挙体

In [9]:
[<System.Flags>]
type MyBitFlags =
    | A = 0b0001
    | B = 0b0010
    | C = 0b0100
    | D = 0b1000
In [10]:
MyBitFlags.A ||| MyBitFlags.C
Out[10]:
A, C {value__ = 5;}

結果の表示が親切です。

In [11]:
enum<MyBitFlags> 6
Out[11]:
B, C {value__ = 6;}
In [12]:
enum<MyBitFlags> 0b1001
Out[12]:
A, D {value__ = 9;}
In [13]:
let x = MyBitFlags.A ||| MyBitFlags.B
In [14]:
x &&& MyBitFlags.A
Out[14]:
A {value__ = 1;}
In [15]:
x &&& MyBitFlags.C
Out[15]:
0 {value__ = 0;}

int32以外の型を使いたい!

サフィックスを付けて優しく定義するだけです。(uyはbyte型のサフィックス)

In [16]:
type ByteEnum =
    | One   = 1uy
    | Two   = 2uy
    | Three = 3uy
In [17]:
ByteEnum.One
Out[17]:
One {value__ = 1uy;}
In [18]:
byte ByteEnum.Two
Out[18]:
2uy
In [19]:
int ByteEnum.Three
Out[19]:
3

byte型の列挙体を定義するのは非常に簡単でしたが、少し困ることもあります。 enum関数を使用することができなくなります。

In [20]:
enum<ByteEnum> 1uy
/home/nbuser/input.fsx(1,1): error FS0001: The type 'int32' does not match the type 'byte'

そんな時にご紹介したいのが、こちらの商品です。

In [21]:
LanguagePrimitives.EnumOfValue<byte, ByteEnum> 1uy
Out[21]:
One {value__ = 1uy;}

今ならなんと、LanguagePrimitives.EnumToValue<'Enum,'T>関数もお付けします! (あまり使わない)

あとがき

  • 出力結果に型情報も欲しい
  • 現時点だとノート名(ファイル名)に日本語を使えないっぽい