🫀

Swift5.9の新機能: Parameter Packsってなんだ?

2023/09/24に公開

Swift5.9で導入されたParameter Packsを体験してみた。まずは動くサンプルを探してみる。

struct Pair<First, Second> {
  var first: First
  var second: Second
  init(_ first: First, _ second: Second) {
    self.first = first; self.second = second
  }
}

func makePairs<each First, each Second>( firsts first: repeat each First,
  seconds second: repeat each Second) -> (repeat Pair<each First, each Second>) {
  return (repeat Pair(each first, each second))
}

let pairs = makePairs(firsts: 1, "hello", seconds: true, 1.0)
print(pairs)
// 'pairs' is '(Pair(1, true), Pair("hello", 2.0))'

swift-evolution/proposals/0393-parameter-packs.md at main · apple/swift-evolution

どうやらこれまでにも有った可変個引数に似た言語機能の様だ。特徴的なのは関数の実装に出てくるeach T, repeat each T、それからeach var, repeate each varという部分。(T: 型パラメーター、var: 変数)

pack reference, pack expansion

ソースを改変してコンパイル・エラーを表示させて見ると、each Teach varはpack referenceと呼ぶことがわかった。repeat each Trepeat each varはpack expansionだ。型パラメーターを前置修飾して新型パラメーターを生み出すのためにanysomeがこれまでにもあったが修飾語eachrepeat eachが言語仕様に付加されている。これまであった後置修飾して可変個型を生み出す(e.g. T...)例を次に説明する。

可変個引数再入門

関数の引数ってのは時には可変個であって欲しい時がある。次のソースは型パラメーターTを使った引数の個数が不定であるGeneric関数の例である。この関数を呼び出す際に同じ型の引数を与えなければならない。ただし、引数の数に制限は無い。T...型の変数keywordsはコレクションなのでfor-in文が使える。

func foo<T>(keywords: T...) -> Int {
  for e in keywords {
    print(e)
  }
  return keywords.count
}
var cnt = foo(keywords: "qua", "bar", "kaz")
print(cnt) // => 3

様々な型パラメーターの可変個引数

上記のfoo関数をparameter packsを使って書き換えると次の様になる。型パラメーターeach Tを可変個の型パラメーターへ変換するにはeach T...ではなくrepeat each Tとすれば良い。許容する引数がT1, T2, T3...型と言った具合に様々な型であることを示している。また、repeat each T型の変数keywordsはTupleとして利用できる。Tupleをコレクションへ変換するにはMirror構造体のchildrenメソッドが使える。下記のbar関数はfoo関数と異なり呼び出す際に異なる型の引数を与える事ができる。

func bar<each T>(keywords: repeat each T) -> Int {
  let myTuple = (repeat each keywords)
  for e in Mirror(reflecting: myTuple).children {
    print(e)
  }
  return Mirror(reflecting: myTuple).children.count
}
var cnt = bar(keywords: 1, true, "kaz")
print(cnt) // => 3

この言語仕様が生み出された背景

Xcode Developer DocumentationをbuildBlockで検索すると引数が1個から10個まで(適当に決められた上限)のGenericメソッドがヒットする。間に合わせのために作られたというかヤッツケ仕事で作られた可変個引数関数(ad-hoc variadic APIs with an arbitrary upper bound)なのだが、作られた当時にはParameter Packsなんていう便利言語仕様は無く、こうするしかなかったのだ。この状況を放置すればメンテナンスコストが大きいために、解決のために生み出されたというのが背景なのだ。Swift Core Team向けには便利機能かもしれない。しかし、App Developerにはユースケースが思いつかない言語仕様だ。

動作検証環境

ターミナル環境でswiftcコマンドを使ってソースコードをコンパイル、実行した。

> sw_vers
ProductName:		macOS
ProductVersion:		13.5.2
BuildVersion:		22G91
> xcodebuild -version
Xcode 15.0
Build version 15A240d

Discussion