Skip to content

第 12 章: エラーハンドリングと型安全性

12.1 はじめに

前章では不変データとコレクション処理を学びました。この最終章では、F# の Option 型Result 型 を使ったエラーハンドリング、そして 計算式(Computation Expressions) による型安全な設計を学びます。

12.2 Option 型

F# には null を直接扱う代わりに Option 型 を使います。値が存在しない可能性を型で明示します。

// Option 型の定義(組み込み)
// type Option<'T> = Some of 'T | None

FizzBuzzList での活用

FindFirst メソッドは Option<FizzBuzzValue> を返します。

member this.FindFirst(predicate: FizzBuzzValue -> bool) =
    this.Values |> List.tryFind predicate

パターンマッチによる処理

let list =
    createList
        [ createValue 1 "1"
          createValue 3 "Fizz"
          createValue 5 "Buzz" ]

match list.FindFirst(fun v -> v.Value = "Fizz") with
| Some value -> printfn "Found: %d:%s" value.Number value.Value
| None -> printfn "Not found"

Option モジュールの活用

// Option.map: Some の場合のみ変換
let fizzNumber =
    list.FindFirst(fun v -> v.Value = "Fizz")
    |> Option.map (fun v -> v.Number)
// Some 3

// Option.defaultValue: None の場合のデフォルト値
let numberOrDefault =
    list.FindFirst(fun v -> v.Value = "Missing")
    |> Option.map (fun v -> v.Number)
    |> Option.defaultValue 0
// 0

// Option.bind: Some の場合のみ次の Option を返す関数を適用
let findAndFilter =
    list.FindFirst(fun v -> v.Value = "Fizz")
    |> Option.bind (fun v ->
        if v.Number > 2 then Some v
        else None)

12.3 Result 型

Result 型 は操作の成功/失敗を型で明示します。例外をスローする代わりに、Ok または Error を返します。

// Result 型の定義(組み込み)
// type Result<'T, 'TError> = Ok of 'T | Error of 'TError

FizzBuzz でのエラーハンドリング

不正な入力値に対するバリデーションを Result 型で表現します。

let validateNumber (number: int) : Result<int, string> =
    if number <= 0 then Error "数値は正の整数でなければなりません"
    elif number > 1000 then Error "数値は1000以下でなければなりません"
    else Ok number

let safeGenerate (fizzBuzzType: FizzBuzzType) (number: int) : Result<FizzBuzzValue, string> =
    number
    |> validateNumber
    |> Result.map (generate fizzBuzzType)

テストで確認

[<Fact>]
let ``正の整数でバリデーション成功`` () =
    let result = validateNumber 5
    match result with
    | Ok n -> Assert.Equal(5, n)
    | Error _ -> Assert.Fail("Expected Ok")

[<Fact>]
let ``ゼロ以下でバリデーション失敗`` () =
    let result = validateNumber 0
    match result with
    | Ok _ -> Assert.Fail("Expected Error")
    | Error msg -> Assert.Equal("数値は正の整数でなければなりません", msg)

[<Fact>]
let ``safeGenerateで安全にFizzBuzzを生成`` () =
    let result = safeGenerate Standard 15
    match result with
    | Ok value -> Assert.Equal("FizzBuzz", value.Value)
    | Error _ -> Assert.Fail("Expected Ok")

Result モジュールの活用

// Result.map: Ok の場合のみ変換
let stringResult =
    safeGenerate Standard 15
    |> Result.map (fun v -> v.Value)
// Ok "FizzBuzz"

// Result.bind: Ok の場合のみ次の Result を返す関数を適用
let chainedResult =
    validateNumber 15
    |> Result.bind (fun n ->
        if n % 3 = 0 || n % 5 = 0 then Ok n
        else Error "FizzBuzz に該当しない数値です")
    |> Result.map (generate Standard)

12.4 計算式(Computation Expressions)

F# の 計算式 は、モナド的な処理を直感的に記述するための構文です。let! を使って Result や Option のチェーンを読みやすく書けます。

result 計算式ビルダー

type ResultBuilder() =
    member _.Bind(result, f) =
        match result with
        | Ok value -> f value
        | Error e -> Error e
    member _.Return(value) = Ok value
    member _.ReturnFrom(result) = result

let result = ResultBuilder()

使用例

let processNumber (fizzBuzzType: FizzBuzzType) (input: int) =
    result {
        let! validNumber = validateNumber input
        let fizzBuzzValue = generate fizzBuzzType validNumber
        return fizzBuzzValue.Value
    }

// テスト
[<Fact>]
let ``計算式で安全に処理できる`` () =
    match processNumber Standard 15 with
    | Ok value -> Assert.Equal("FizzBuzz", value)
    | Error _ -> Assert.Fail("Expected Ok")

[<Fact>]
let ``計算式でエラーが伝播する`` () =
    match processNumber Standard 0 with
    | Ok _ -> Assert.Fail("Expected Error")
    | Error msg -> Assert.Equal("数値は正の整数でなければなりません", msg)

計算式の let! は Rust の ? 演算子に相当します。Error が発生した時点で処理を中断し、エラーを伝播します。

12.5 Option と Result の使い分け

用途
Option<'T> 値の有無を表現 検索結果が見つからない場合
Result<'T, 'E> 成功/失敗とエラー情報を表現 バリデーション失敗時にエラーメッセージを返す
// Option: 見つからないだけ(エラー情報不要)
let found = list.FindFirst(fun v -> v.Value = "Fizz")

// Result: なぜ失敗したかの情報が必要
let validated = validateNumber -1

12.6 他言語との比較

概念 F# Rust Java
エラーハンドリング Result<'T, 'E> Result<T, E> try-catch
null 安全 Option<'T> Option<T> Optional<T>
エラー伝播 計算式 let! ? 演算子 throws
パターンマッチ match...with match switch(Java 21+)
モナド構文 計算式

F# の計算式は Haskell の do 記法に相当する機能で、Rust にはない F# 独自の強力な機能です。

12.7 まとめ

この章では以下を学びました。

概念 F# の実現方法
Option 型 Some v / None + パターンマッチ
Result 型 Ok v / Error e + パターンマッチ
Option モジュール map, bind, defaultValue
Result モジュール map, bind
計算式 result { let! ... return ... }
エラー伝播 let! による自動伝播

全 12 章を通じて、F# の TDD 基本サイクル、開発環境の自動化、関数型アプローチによる設計、高度な関数型プログラミングを一通り学びました。F# のパイプライン演算子、判別共用体、パターンマッチ、不変データ、Result/Option は、安全で堅牢なソフトウェアを構築するための強力な基盤です。