F#のフォーマット
F# にはフォーマットのガイドライン( GitHub | MSDN )がありますが、公式の翻訳がおかしかったり、一部に曖昧さを残している点があります。この記事はその中でも特にMSDNの記述に曖昧さが残る以下の式のコーディング規約を調べてきたので記事にしました。
プロジェクトで統一したコーディング規約を設定したい!という際に参考にして下さい。
- レコード (record)
- リスト (list)
- 配列 (array)
- シーケンス式 (seq式 / sequence expression)
- コンピュテーション式 (computation expression)
- オブジェクト式 (object expression)
原則としてこれらの式には共通のコーディング規約が適用できます。
式が十分に短い場合は1行で書く
基本ルールです。F#では情報量の少ない行は嫌われます。
共通ルールとして、波括弧 {
}
や角括弧 [
]
の内側には1つスペースをあけます。1
// レコード
{ X = 10; Y = 20 }
// リスト(配列も同じ)
[ 1; 2; 3; 4 ]
// シーケンス式(コンピュテーション式も同じ)
seq { for i in 1..10 -> i * 5 }
// オブジェクト式 (MSDNには1行の例がある。個人的には非推奨)
{ new IDisposable with member _.Dispose() = printfn "Bananas!" }
長い場合は複数行で書く
ややこしいのはここから。
最初に書いたようにF#のフォーマットガイドには Anh-Dung Phan が書いた GitHub上にある基本ガイドと、それを元に拡張されたMSDNのガイド が存在するのですが、 それぞれで内容が異なります。
複数行の書き方(Anh-Dung Phan式)
- 開き括弧の前に必ず改行
- 開き括弧の次に1つスペースをあけて最初のトークンを配置
- その後の行はインデントを最初のトークンに合わせる2
- 閉じ括弧は最後のトークンと同じ行に、1つスペースをあけて配置
ただし、1行目にwithがある式ではインデントを最初の行に揃えずに1段深くします。
// レコード(各フィールドごとに必ず改行する)
{ X = sin v + cos w
Y = cos v - sin w
Z = v * v + w * w }
//^インデントはこのラインに合わせる
// NG: 同じ行に2つのフィールドを書かない
{ X = sin v + cos w; Y = cos v - sin w
Z = v * v + w * w }
// NG: 前の行の続きから書かない
let point = { X = sin v + cos w
Y = cos v - sin w
Z = v * v + w * w }
// リスト(1要素ごとか、意味のあるまとまりごとに改行する)
[ SuperLongLongColorName.Red
SuperLongLongColorName.Green
SuperLongLongColorName.Blue ]
// 配列の場合、2行目以降のインデントはスペース3個分になる
[| { Id = 0; Color = "Red" }
{ Id = 1; Color = "Green" }
{ Id = 2; Color = "Blue" } |]
// レコードwith式やオブジェクト式では、インデントを1行目に揃えずに1段深くする
// (これらの式はwithの後に1段深いインデントを必要とするため)
{ point with
X = sin v + cos w
Y = cos v - sin w }
{ IDisposable with
member _.Dispose() = printfn "Bananas!" }
6行以上になる場合は閉じ括弧を新しい行に配置してもよいです。
{ Color1 = "Red"
Color2 = "Orange"
Color3 = "Yellow"
Color4 = "Green"
Color5 = "Cyan"
Color6 = "Blue"
Color7 = "Magenta"
}
なお、こちらのガイドラインにはシーケンス式やコンピュテーション式に関する記述はありません。
このフォーマットのメリットは行数が少なくなることですが、中途半端なインデントが生まれるデメリットもあります。
複数行の書き方(MSDN式)
MSDNでは、Anh-Dung Phan式の書き方も提示した上で、多段インデントを含む場合や関数に適用する場合は次のようなフォーマットを推奨しています。
- 開き括弧の後(
with
句があるならその後)に改行 - インデントを1段深くして最初のトークンを配置する
- 閉じ括弧は新しい行に配置し、開き括弧がある行とインデントを揃える
// レコード
{
X = sin v + cos w
Y = cos v - sin w
Z = v * v + w * w
}
// レコードのwith式
{ point with
X = sin v + cos w
Y = cos v - sin w
}
// リスト(配列も同じ)
[
SuperLongLongColorName.Red
SuperLongLongColorName.Green
SuperLongLongColorName.Blue
]
// シーケンス式・コンピュテーション式
seq {
for i in superLongLongNameParameter do
if i % 2 = 0 then
i * 10 + superLongLongNameParameter.Length
}
// オブジェクト式
{ new IDisposable with
member _.Dispose() = printfn "Bananas!"
}
これらの式が十分短い関数の最後の引数である場合、開き括弧は前の行の最後に配置します。3
シーケンス式やコンピュテーション式もこのパターンの扱いです。
Some {
X = sin v + cos w
Y = cos v - sin w
}
// 関数が長い・多い場合はパイプ演算子を使う
{
X = sin v + cos w
Y = cos v - sin w
}
|> superLongLongFunctionName
|> HyperLongLongType.LongNameMethod
こちらを使うメリットは中途半端なインデントが生まれないことです。そのため、これらの式の中に更に深いインデントを含む場合にコードが書きやすくなります。4
ただし、Anh-Dung Phan式に比べて最大2行増えるというデメリットもあります。
// MSDN式の書き方なら、下式のValueのようにインデントが深くなっても書きやすい
{
X = sin v + cos w
Y = cos v - sin w
Value =
if w > 0.0 then
cos v * cos w
else
sin v * sin w
}
結局どっちがいいのか?
好みです。 が、多人数開発でコーディング規約として使うなら他言語使いから見ても親しみやすいMSDN式をお勧めします。
私も基本的にはMSDN式で書きますが、「ブロックの最後の行がこれらの式だけて構成されていて、各行が十分に単純な場合」にはAnh-Dung Phan式も使います。
また、この2つを混ぜたようなフォーマットはやめたほうがいいでしょう。少なくとも開き括弧か閉じ括弧のどちらかは行頭にあるべきです。
// 開き括弧も閉じ括弧も行頭にない書き方はやめたほうがいいと思う
Some {
X = sin v + cos w
Y = cos v - sin w }
まとめ
コーディング規約は書きやすく読みやすいように決めましょう。