Swift5.7 新機能まとめ

## まえがき
Swift5.7から導入される新機能をいくつか抜粋し、触りの部分のみまとめました。
各機能の詳細については、プロポーザルのリンクをご参照ください。また内容に間違いがある場合や正確性に欠く記載がありましたらコメントいただけると幸いです。

## まずは分かりやすい箇所から…

### [SE-0345](https://github.com/apple/swift-evolution/blob/main/proposals/0345-if-let-shorthand.md) **`if let` shorthand for shadowing an existing optional variable**

**if letのUnwrapがより短くシンプルになりました**

```swift
let foo: String? = "foo"

if let foo {
    print("foo is unwrapped: \(foo)")
}
```

またif文以外にもguard文やwhile文でも同様に可能です。weak selfをguard文でunwrapする時などよりシンプルに書けそうです。

```swift
if let foo { ... }
if var foo { ... }

else if let foo { ... }
else if var foo { ... }

guard let foo else { ... }
guard var foo else { ... }

while let foo { ... }
while var foo { ... }
```

[swift-evolution/0345-if-let-shorthand.md at main · apple/swift-evolution](https://github.com/apple/swift-evolution/blob/main/proposals/0345-if-let-shorthand.md)

### [SE-0326](https://github.com/apple/swift-evolution/blob/main/proposals/0326-extending-multi-statement-closure-inference.md) ****Enable multi-statement closure parameter/result type inference****

**クロージャーの返り値の型推論が少し強くなりました**

複数の文で構成されるクロージャーの返り値の型が推論されるようになりました。

下記にサンプルコードを記載しています。

```swift
func map<T>(fn: (Int) -> T) -> T {
    return fn(37)
}

// これはSwift5.6以下もOK
let _ = map { Double($0) }

// でも複数の文が入ってくると、ちゃんと返り値の型とか入れないとダメだった
let _ = map { val -> Double in
    print("Hehe")
    return Double($0)
}

// 5.7からは省略可能に
let _ = map {
    print("well done")
    return Double($0)
}
```

個人的な感想ですが、シンプルに書けるようになるものの、そこまで恩恵は大きく無さそうな印象です（どちらかといえばコンパイラ側の実装の変更範囲大変そうだなぁという感想が勝つ…）

### **[SE-0341](https://github.com/apple/swift-evolution/blob/main/proposals/0341-opaque-parameters.md) Opaque Parameter Declarations**

**Opaque Typeを関数の引数に指定できるようになりました**

```swift
// NEW
func f(_ p: some P) {}

// is identical to
func f<T: P>(_ p: T) {}
```

「え、その手のサブタイピングって `f(_ p: P)`　て書けば良くない？」とパッと見思いました。しかし、例えばSequenceのようにassociated typeや、EquatableのようにSelfを使用するprotocolの場合においては例外で、ジェネリクスの制約として使う他ありませんでした。

そのためにわざわざジェネリクスを用意して書いてあげるのは冗長じゃない？という課題感から起案されたプロポーザルであり改善内容となっているようです。

### [SE-0328](https://github.com/apple/swift-evolution/blob/main/proposals/0328-structural-opaque-result-types.md) ****Structural opaque result types****

**`some` キーワードが関数の返り値により柔軟に適用できるようになりました**

コード例を以下に示します。

```swift
func f() -> [some Hashable: some Codable] {
    return [ 1: "One", 2: "Two" ]
}

func g() -> [some Equatable] {
    // ※ Opaque Typeなので["foo", 2]はTypeError
    return ["foo", "bar"]
}

// おそらく文法的には正しいがXcode Version 14.0 beta (14A5228q)だとLLDBサーバーがクラッシュする?
func h() -> () -> some Equatable {
    return {
        let str = "hoge"
        return str
    }
}
```

こんな感じで従来では `-> some View`  のように単純な返り値型でしかOpaque Typeを使用できなかったところが、ディクショナリ関数の返された関数の返り値（引数はエラー）など幅広い使用用途がサポートされるようになりました！

### [SE-0309](https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md) ****Unlock existentials for all protocols****

**Selfやassociated typeを持つprotocolを型として使うことが可能になりました**

```swift
let foo: any Equatable = "foo"
let bar: any Equatable = "bar"

foo == bar // -> Error. Selfが同一の型である保証が無いため

if let foo = foo as? String,
   let bar = bar as? String {
    print(foo == bar) // False
}
```

当然以下のように定義することも可能です。

```swift
let foo: [any Equatable] = ["foo", 1] // OK
let bar: [some Equatable] = ["bar", 2] // ちなみにsomeの場合はNG, 同じ型の要素でないため
```

この機能によって型消去テクニックが不要になります。

### [SE-0346](https://github.com/apple/swift-evolution/blob/main/proposals/0346-light-weight-same-type-syntax.md) ****Lightweight same-type requirements for primary associated types,**** [SE-0353](https://github.com/apple/swift-evolution/blob/main/proposals/0353-constrained-existential-types.md) ****Constrained Existential Types****

**protocolにジェネリクスを持たせることができるようになりました**

[SE-0309](https://github.com/apple/swift-evolution/blob/main/proposals/0309-unlock-existential-types-for-all-protocols.md)によってSelfやassociated typeを持つProtocolにおいても型として利用することが可能になりました。一方、そのassociated typeについて制約を課したい状況はあるはずです。

その際に以下のようにprotocolにジェネリクスを設定して型を渡すことでシンプルに解決できるようになっています。

```swift
protocol Sequence<Element> {
    associatedtype Element
    associatedtype Iterator : IteratorProtocol
    where Element == Iterator.Element
    ...
}

protocol DictionaryProtocol<Key, Value> {
    associatedtype Key : Hashable
    associatedtype Value
    ...
}
```

このように渡せるようになることで、型消去を行う必要性や、where句をゴニョゴニョする必要がなくなりスッキリした記述になります。

```swift
// 従来の方法(1) whereを使用する方法
func foo<S: Sequence>() -> S where S.Element == Int {}
// 従来の方法(2) AnyHogeで型消去した後ジェネリクスに渡す方法
func foo() -> AnySequence<Int> {}

// NEW
func foo() -> some Sequence<Int>
```

## 正規表現周り

### [SE-0350](https://github.com/apple/swift-evolution/blob/main/proposals/0350-regex-type-overview.md) ****Regex Type and Overview,**** [SE-0354](https://github.com/apple/swift-evolution/blob/main/proposals/0354-regex-literals.md) ****Regex Literals****

**正規表現がより扱いやすくパワフルになりました**

例えば以下の文字列(`statement`)をパターンマッチを用いて適切に情報を抜き出して、 `Transaction` にデコードする処理を考えてみます。

```swift
struct Transaction {
    enum Kind { case credit; case debit }

    let kind: Kind
    let date: Date
    let accountName: String
    let amount: Decimal
}

let statement = """
    CREDIT    03/02/2022    Payroll                   $200.23
    CREDIT    03/03/2022    Sanctioned Individual A   $2,000,000.00
    DEBIT     03/03/2022    Totally Legit Shell Corp  $2,000,000.00
    DEBIT     03/05/2022    Beanie Babies Forever     $57.33
"""
```

従来では、`NSRegularExpression` を用いて正規表現を組み立てて、`Range` をゴニョゴニョして抜き出すような処理になります。

```swift
// 従来の方法
let pattern = #"(\w+)\s\s+(\S+)\s\s+((?:(?!\s\s).)*)\s\s+(.*)"#
let nsRegEx = try! NSRegularExpression(pattern: pattern)

let target = "CREDIT  03/02/2022  Payroll  $200.23"
let range = NSRange(target.startIndex..<target.endIndex, in: target)
guard let result = nsRegEx.firstMatch(in: target, range: range),
      let kindRange = Range(result.range(at: 1), in: target) else {
    fatalError()
}
let kind = Transaction.Kind(target[kindRange]) // CREDIT
```

今回より新しく導入された `Regex` を用いることでよりシンプルに同等の処理を実装することが可能になります。特に下記2.3.にあるリテラル型でラベルを用いることで、パターンマッチの結果が静的に決められるため型安全に扱うことが出来ます。

```swift
// 1.コンストラクタで正規表現オブジェクトを生成する方法
let pattern = #"(\w+)\s\s+(\S+)\s\s+((?:(?!\s\s).)*)\s\s+(.*)"#
let regex = try! Regex(pattern) 
// -> Regex<AnyRegexOutput>

// 2.リテラルで生成する方法
let regex = /(\w+)\s\s+(\S+)\s\s+((?:(?!\s\s).)*)\s\s+(.*)/ 
// -> Regex<(Substring, Substring, Substring, Substring, Substring)>

// 3.リテラル（ラベル付き）で生成する方法
let regex = #/
    (?<kind>    \w+)                \s\s+
    (?<date>    \S+)                \s\s+
    (?<account> (?: (?!\s\s) . )+)  \s\s+
    (?<amount>  .*)
  /#
// ->regex: Regex<(
//    Substring,
//    kind: Substring,
//    date: Substring,
//    account: Substring,
//    amount: Substring
//  )>
if let result = try? regex.wholeMatch(in: "CREDIT  03/02/2022  Payroll  $200.23" {
    print(result.0) // CREDIT  03/02/2022  Payroll  $200.23
    print(result.1) // CREDIT
    print(result.kind) // CREDIT
    print(result.2) // 03/02/2022
    print(result.date) // 03/02/2022
    print(result.3) // Payroll
    print(result.account) // Payroll
}
```

### [SE-0351](https://github.com/apple/swift-evolution/blob/main/proposals/0351-regex-builder.md) ****Regex builder DSL****

**DSLでより可読性高く正規表現を組み立てられるようになりました**

上記で紹介した `Regex` により正規表現の扱いが楽になりそうですが、更に正規表現の作成をより宣言的で可読性高く行うためのDSLが `RegexBuilder` パッケージから提供されるようになったようです。

例えば先程の `statement` 用の正規表現は以下のように`RegexBuilder`を用いると作成することが出来ます。

```swift
import RegexBuilder

enum TransactionKind: String {
  case credit = "CREDIT"
  case debit = "DEBIT"
}

struct Date {
  var month, day, year: Int
  init?(mmddyyyy: String) { ... }
}

struct Amount {
  var valueTimes100: Int
  init?(twoDecimalPlaces text: Substring) { ... }
}

let statementPattern = Regex {
  // Parse the transaction kind.
  TryCapture {
    ChoiceOf {
      "CREDIT"
      "DEBIT"
    }
  } transform: {
    TransactionKind(rawValue: String($0))
  }
  OneOrMore(.whitespace)
  // Parse the date, e.g. "01012021".
  TryCapture {
    Repeat(.digit, count: 2)
    Repeat(.digit, count: 2)
    Repeat(.digit, count: 4)
  } transform: { Date(mmddyyyy: $0) }
  OneOrMore(.whitespace)
  // Parse the transaction description, e.g. "ACH transfer".
  Capture {
    OneOrMore(.custom([
      .characterClass(.word),
      .characterClass(.whitespace)
    ]))
    CharacterClass.word
  } transform: { String($0) }
  OneOrMore(.whitespace)
  "$"
  // Parse the amount, e.g. `$100.00`.
  TryCapture {
    OneOrMore(.digit)
    "."
    Repeat(.digit, count: 2)
  } transform: { Amount(twoDecimalPlaces: $0) }
} // => Regex<(Substring, TransactionKind, Date, String, Amount)>

let statement = """
  CREDIT    04062020    PayPal transfer    $4.99
  CREDIT    04032020    Payroll            $69.73
  DEBIT     04022020    ACH transfer       $38.25
  DEBIT     03242020    IRS tax payment    $52249.98
  """
for match in statement.matches(of: statementPattern) {
  let (line, kind, date, description, amount) = match.output
  ...
}
```

このように宣言的で且つ可読性高く正規表現を組み立てられる他、transformで変換処理を渡すことで１つの`Regex`オブジェクトで、文字列からオブジェクトに変換するための情報を集約することも可能になります。

詳細なAPIは[https://github.com/apple/swift-evolution/blob/main/proposals/0351-regex-builder.md](https://github.com/apple/swift-evolution/blob/main/proposals/0351-regex-builder.md)を参照ください。

### [SE-0357](https://github.com/apple/swift-evolution/blob/main/proposals/0357-regex-string-processing-algorithms.md) ****Regex-powered string processing algorithms****

**導入されたRegexで、より文字列操作APIがパワフルになりました**

Collectionに対して以下のメソッドが追加されています。

| 関数 | 説明 |
| --- | --- |
| func contains<R>(_ regex: R) -> Bool where R : RegexComponent | 引数に渡したRegexComponentとマッチするかどうか
str.contains(/foo/) // Bool |
| func starts<R>(with regex: R) -> Bool where R : RegexComponent | RegexComponentで始まっているか |
| func firstRange<R>(of regex: R) -> Range<Self.Index>? where R : RegexComponent | 与えられたRegexComponentと初めてマッチした範囲を返す |
| func ranges<R>(of regex: R) -> [Range<Self.Index>] where R : RegexComponent | 与えられたRegexComponentとマッチした範囲を返す |
| func split<R>(by separator: R) -> [Self.SubSequence] where R : RegexComponent | separatorにマッチする部分を取り除いて分割する |
| func firstMatch<R>(of r: R) -> Regex<R.RegexOutput>.Match? where R : RegexComponent | 与えられたRegexComponentと初めてマッチした箇所を返す |
| func matches<R>(of r: R) -> [Regex<R.RegexOutput>.Match] where R : RegexComponent | 与えられたRegexComponentとマッチした箇所全てを配列で返す |
| 以下String, Substring |  |
| func wholeMatch<R>(of r: R) -> Regex<R.RegexOutput>.Match? where R : RegexComponent | 全体としてマッチする箇所を返す。 |
| func prefixMatch<R>(of r: R) -> Regex<R.RegexOutput>.Match? where R : RegexComponent | 先頭からマッチする箇所を返す。 |

またSwitch文においても以下のような使用方法も出来るようです。

```swift
switch "abcde" {
    case /a.*f/:  // never taken
    case /abc/:   // never taken
    case /ab.*e/: return "success"
    default:      // never taken
}

switch "2022-04-22" {
    case decimalParser: // never taken

    case OneOrMore {
        CharacterClass.whitespace
    }: // never taken

    case #/\d{2}/\d{2}/\d{4}/# // never taken

    case dateParser: return "success"

    default: // never taken
}
```

### [SE-0329](https://github.com/apple/swift-evolution/blob/main/proposals/0329-clock-instant-duration.md) ****Clock, Instant, and Duration****

**時間や期間を表すオブジェクトが追加されました**

主に追加された概念は以下の3パターンです。

1. Clock: 時計、時間の測定などの用途
    1. Continuous Clock: 実世界と同じように動き続ける時計
    2. Suspending Clock: 逆に動かない、通常のDateオブジェクトに近いイメージ?
2. Instants: 瞬間を表現
3. Durations: 期間

**Clock**

「今」という概念 `now`や、特定時間のスリープ `sleep()`、クロージャの処理時間の計測`measure()` を提供する概念で、以下のprotocolで定義されています。

```swift
public protocol Clock: Sendable {
    associatedtype Duration: DurationProtocol
    associatedtype Instant: InstantProtocol where Instant.Duration == Duration

    var now: Instant { get }

    func sleep(until deadline: Instant, tolerance: Instant.Duration?) async throws 

    var minResolution: Instant.Duration { get }
}

extension Clock {
    func measure(_ work: () async throws -> Void) reasync rethrows -> Instant.Duration
}
```

このClockを実装するオブジェクトとして、`ContinuousClock` と `SuspendingClock` が用意されています。

`ContinuousClock` はマシンがスリープ中であっても中断することなく、そういった状況下で精度良く操作したい場合に有効とのことです。一方、`SuspendingClock` ではスリープ中は中断するようです。（このあたりの使い分け方法はよくわからず、、詳しい方はコメントいただけると助かります。）

### 参考

[swift/CHANGELOG.md at main · apple/swift](https://github.com/apple/swift/blob/main/CHANGELOG.md)