F#

.NET 上の関数型優先(functional-first)言語(2005, Don Syme / Microsoft Research)。OCaml の系譜(ML ファミリ)を .NET に持ち込んだもの。純粋ではないが、既定が不変・式指向。

型と設計

判別共用体+パターンマッチの実例

判別共用体(DU)で「とりうる状態だけ」を型として定義し、match で網羅的に分岐する。コンパイラが漏れたケースを警告する:

type PaymentMethod =
    | Cash
    | Card of number: string * expiry: string
    | PayPal of email: string

let describe method =
    match method with
    | Cash                       -> "現金"
    | Card (number, _)           -> sprintf "カード末尾 %s" (number.Substring(number.Length - 4))
    | PayPal email               -> sprintf "PayPal: %s" email

// Option も DU。None を消し忘れるとコンパイラが警告
let safeDiv a b =
    match b with
    | 0 -> None
    | _ -> Some (a / b)

Card ケースを削ると「Card が未処理」と警告。これが 不正な状態を表現不能にするドメインモデリングの中核。

units of measure

型レベルで物理単位を付与し、次元の不一致をコンパイル時に弾く。実行時コストはゼロ(消去される):

[<Measure>] type m     // メートル
[<Measure>] type s     // 秒

let distance = 100.0<m>
let time     = 9.58<s>
let speed    = distance / time      // 型は float<m/s>

// let wrong = distance + time      // ← コンパイルエラー: m と s は足せない

m/s と m を取り違えるバグが型で防がれる。NASA の単位取り違え事故クラスの誤りを言語が排除する。

computation expressions

async { } / seq { } / task { } や自作の let! 構文で、モナド的な do 記法を一般化して書ける。非同期・列・オプションなどを同じ構文で扱う。

let fetchAll () = async {
    let! a = fetchAsync "https://a"   // let! が await 相当(bind を脱糖)
    let! b = fetchAsync "https://b"
    return a + b
}

let! は裏で builder.Bind 呼び出しに脱糖される。自作の builder を定義すれば Option/Result/独自モナドにも同じ { ... } 構文を使える。

立ち位置

.NET 相互運用で OOP も書けるマルチパラダイム。金融・ドメインロジック重視の業務系で「型で正しさを縛る」用途に強い。静的ブログ生成や Web(Giraffe/Saturn)まで実用域。

押さえどころ(カード化候補)

関連

Graph