SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

アップルの新プログラミング言語「Swift」を探検しよう

Objective-Cより柔軟かつ安全なプログラミングを可能にするSwiftの「ジェネリクス」

アップルの新プログラミング言語「Swift」を探検しよう 第7回


  • X ポスト
  • このエントリーをはてなブックマークに追加

列挙型と型引数を用いる際の注意点

 列挙型と型引数を用いる際、2つの型引数を用いた宣言や、再帰的なデータ型を型引数を用いた宣言を、次のように行いたいことがあります。

enum Result<T,S> {
    case Success(T)
    case Failure(S)
}

enum List<T> {
    case Nil
    case Cons(T, List<T>)
}

 2015年2月現在、Swiftでは列挙型に2つの型引数を用いたり、再帰的なデータ型の定義のために列挙型のメンバー付随型に自分自身の型を用いたりすると、コンパイルエラーが起こります。

 ここでのResult<T, S>型は、「成功した場合と失敗した場合を示す2つのメンバーを持った結果」を表す列挙型として使用できます。また、再帰的リスト型は、「続きの値がない場合とある場合を示す2つのメンバーを持ったリスト」を表す列挙型として使用できます。

 このような列挙型の実現のために、SwiftzBrightFuturesなどのオープンソースソフトウェアは、次のようなBox<T>クラスを付随型宣言の間にかませることでエラーを解消しています。

改善例
final class Box<T> {
    let value: T
    init(_ value: T) {
        self.value = value
    }
}

enum Result<T, S>: Printable {
    case Success(Box<T>)
    case Failure(Box<S>)
    
    var description: String {
        switch self {
        case .Success(let val) : return "Success(\(val.value))"
        case .Failure(let val) : return "Failure(\(val.value))"
        }
    }
}

enum List<T>: Printable {
    case Nil
    case Cons(Box<T>, Box<List<T>>)
    
    var description: String {
        switch self {
        case .Nil : return "[]"
        case .Cons(let val, let cons) : return "[\(val.value), \(cons.value.description)]"
        }
    }
}

let success = Result<Int, String>.Success(Box(1))
success.description // Success(1)
let failure = Result<Int, String>.Failure(Box("failed"))
failure.description // Failure(failed)

let list: List<Int> = List.Cons(Box(1), Box(List.Cons(Box(2), Box(List.Nil))))
list.description // [1, [2, []]]

 この改善例では、ジェネリックタイプの型引数をメンバー付随型の中で使う際に必ずBox<T>クラスを挟んでいます。このような改善が必要な理由は不明です。「Swiftのコンパイラが、再帰的列挙型などの内部的なデータ長を予測できないから」といった憶測がありますが、詳細はAppleの開発者のみぞ知るといったところでしょうか。

ジェネリックタイプに対するエクステンション

 エクステンションの中では、拡張元のジェネリックタイプに宣言された型引数を自由に使えます。このとき、元の型引数の宣言をする必要はありません。

宣言例
extension ジェネリックタイプ名 {
    ジェネリックタイプに宣言された型引数を宣言なしに用いてよい
}

 先ほどのQueue<T>ストラクチャにエクステンションを用いて確認してみましょう。

struct Queue<T> {
    
    private var values = [T]()
    
    mutating func put(value: T) {
        values.append(value)
    }
    
    mutating func get() -> T? {
        let value = values.first
        if values.count > 0 { values.removeAtIndex(0) }
        return value
    }
}

extension Queue {
    subscript(index: UInt) -> T? {
        let idx = Int(index)
        return idx < values.count ? values[idx] : nil
    }
}

var queue = Queue<String>()
queue.put("aa")
queue.put("456")
queue[0] // aa
queue.get() // aa
queue[1] // nil

 Queue<T>に対して新しく宣言するエクステンションで、型引数を宣言することなくQueue本体に定義された型引数Tが使えていることに注目してください。ここではUInt型を用いたsubscriptでQueueの内部にアクセスをかけ、T?型を返すように宣言しています。indexがQueueの長さに達していなければOptional.Noneを返すようにしています。

次回は

 今回はジェネリクスを中心に解説しました。型引数とプロトコルによる制約を中心としたプログラミングスタイルはSwiftのAPIでも使われている箇所が多いため、ぜひとも押さえておきたいところです。次回はいよいよ最終回。メタタイプと演算子の説明を中心に行います。お楽しみに。

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
アップルの新プログラミング言語「Swift」を探検しよう連載記事一覧

もっと読む

この記事の著者

yad(ヤド)

クラスメソッド株式会社のアプリケーションエンジニア。iPhoneアプリケーションの開発に2年以上従事している。開発に使用するObjective-Cのみならず、関数型言語Haskellや機械学習などにも関心がある。「Developers.IO」に寄稿した記事の一覧

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/8449 2015/03/02 12:51

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング