nakaoka3の技術ブログ

2023年中に52本なにか書きます

Swiftで代数的データ型とパターンマッチング

以前の記事でKotlinで代数的データ型(Algebraic Data Type, ADT)とパターンマッチングの使い方を確認した。同様にSwiftでも確認する。なお本記事で使っているSwiftのバージョンは5.9である。

nakaoka3.hatenablog.com

直和型 Sum Type

直和型にはenumを使う。

enum MusicGenre {
    case rock
    case pop
    case jazz
    case classic
}

enum YearsActive {
    case stillActive(_ start: Int)
    case activeBetween(_ start: Int, _ end: Int)
}

直積型 Product Type

直積型にはstruct を使う。

typealias Location = String

struct Artist {
    let name: String
    let genre: MusicGenre
    let origin: Location
    let yearsActive: YearsActive
}

パターンマッチング

パターンマッチングにはswitchを使う。switch は網羅的でないとコンパイルエラーになる。

func prettyPrint(_ artist: Artist) -> String {
    // Note: switch must be exhaustive
    switch artist.yearsActive {
    case .stillActive(let start):
        return "\(artist.name) (\(start) - )"
    case .activeBetween(let start, let end):
        return "\(artist.name) (\(start) - \(end))"
    }
}

以下は filter の中で使う例だ。以下の例なら、switchではなくifやguard を使っても同様の処理が書けた。

なおSwiftではif/switch/guardは文である。式になっている言語ほうが好みではある。

let stillActiveArtists = artists.filter { artist in
    // switch を使う場合
    // switch artist.yearsActive {
    // case .stillActive:
    //     return true
    // default:
    //     return false
    // }

    // if を使う場合
    // if case .stillActive = artist.yearsActive {
    //     return true
    // }
    // return false
    
    // guard を使う場合
    guard case .stillActive = artist.yearsActive else {
        return false
    }
    return true
}
print("# Still Active Artists")
stillActiveArtists.forEach { artist in
    print(artist.name)
}

まとめ

  • Swiftで直和型は enum, 直積型は struct を使うことで実現できる。
  • 直和型に対する パターンマッチングは switch を使うことで実現できる。場合によってはif や guard を使ってもよい。