LoginSignup
14
6

More than 5 years have passed since last update.

Swift EvolutionのAcceptedステータスまとめ (2017.1.27) - Protocol-oriented integers, Permit where clauses to constrain associated types, ...etc

Last updated at Posted at 2017-01-26

Swift3が落ち着いて、そろそろSwift4に向けてウォームアップを考え始めなくてはいけない頃ではないでしょうか。そう、休みなんてないんです😭 Swift Evolutionの見やすいサイトができていい機会だったので、2017.1.27時点の Accepted で未実装のものをまとめたいと思います。

SE-0068 Expanding Swift Self to class members and value types

Swift3.0からは dynamicType から type(of:) になった自身のクラスまたは型を取得する方法が Self で可能になります。下記のようなスタティックメソッドをインスタンスメソッドから使用する際に、名前が長いとうんざりしてたので地味に嬉しいです。

struct FooFooFooFooFoo {
    static func bar() { }
    func baz() {
        FooFooFooFooFoo.bar() // Before
        type(of: self).bar()  // Before
        Self.bar() // After
    }
}

SE-0042 Flattening the function type of unapplied method references

Unapplied Method Reference (インスタンスメソッドを型から直接呼び出して関数として得るもの)の返り値がフラットな状態で受け取れるようになります。通常カーリー化では f(1)(2) のように関数を引数として受けとるので現状が正しいように思えますが、Swiftの場合は reduce(0, +) のように引数を並べて受け取るように作られているので、そこに合わせるといった感じでしょうか。

下記のように mapUnapplied Method Reference をそのまま渡しても、関数のネストがひとつ深いのでエラーが出てしまします。

func sortedArrays<T: Comparable>(arrays: [[T]]) -> [T] {

    // Error: `map` expects [T] -> [T], but
    // `Array.sorted` has type ([T]) -> () -> [T]
    return arrays.map(Array.sorted)
}
struct Foo {
    let x: Int
    func bar(y: Int) -> Int {
        return x + y
    }
}

let f = Foo.bar // (Foo) -> (Int) -> Int

let g = f(Foo(x: 2))(3) // Before
let g = f(Foo(x: 2), 3) // After

SE-0075 Adding a Build Configuration Import Test

今まで使える機能(モジュール)は #if !os(OSX) のようにOSで判定するしかなかったのですが、今後そのOSにモジュールが対応した場合コードの改修が必要になり、アップデートの多いSwiftでは脆い実装になりがちです。そこで下記のように canImport(Cocoa) でそのモジュールが使用できるか判定できるようになります。

#if canImport(UIKit)
   // UIKit-based code
   #elseif canImport(Cocoa)
   // OSX code
   #elseif
   // Workaround/text, whatever
#endif

SE-0104 Protocol-oriented integers

Swiftの整数のプロトコルはジェネリクスに適したものではないみたいで Int8Int16 などを受け取る整数型のジェネリクスな引数を受け取る処理などを書くのは容易ではありませんでした。このブログでは整数にジェネリクスアルゴリズムを持たせようと試みてます。

let foo: Int8 = 1
let bar = 2

// error: binary operator '<<' cannot be applied
// to operands of type 'Int8' and 'Int'
let baz = foo << bar

今後は以下のようなモデルの体型になるみたいです。上記のような型の違いによる演算が行えない状態を解消され、整数のどの型なのか気にしながら演算する必要がなくなります。あとは整数の拡張が容易になるみたいです。ちょっとどんな用途があるのかわかりませんが…

                +--------------+  +-------------+
        +------>+  Arithmetic  |  | Comparable  |
        |       |   (+,-,*,/)  |  | (==,<,>,...)|
        |       +-------------++  +---+---------+
        |                     ^       ^
+-------+------------+        |       |
|  SignedArithmetic  |      +-+-------+------+
|     (unary -)      |      |  BinaryInteger |
+------+-------------+      |  (words,%,...) |
       ^                    ++---+-----+-----+
       |         +-----------^   ^     ^-----------+
       |         |               |                 |
+------+---------++    +---------+-------------+  ++------------------+
|  SignedInteger  |    |  FixedWidthInteger    |  |  UnsignedInteger  |
|                 |    | (bitwise,overflow,...)|  |                   |
+---------------+-+    +-+-----------------+---+  ++------------------+
                ^        ^                 ^       ^
                |        |                 |       |
                |        |                 |       |
               ++--------+-+             +-+-------+-+
               |Int family |-+           |UInt family|-+
               +-----------+ |           +-----------+ |
                 +-----------+             +-----------+

SE-0142 Permit where clauses to constrain associated types

associatedtypewhere 句が付けられるようになります。これは結構嬉しい状況が出るのではないでしょうか。例えば Sequence プロトコルに準拠した associatedtype を宣言する時に中身の Element の型を指定したい場合などは下記のように書くことができます。関数のジェネリクスと同じ挙動になるので扱いやすくなりそうです。

protocol Foo {
    associatedtype Bar: Sequence where Bar.Iterator.Element == Int
    func baz() -> Bar
}

また、上記の修正で下記のようにプロトコル宣言時にも where 句を使えるようになるみたいです。

protocol Foo : Sequence where Iterator.Element == Int { ... }

しかし、このプロトコル宣言時に使える where 句は親のプロトコル(この場合 Sequence )に関連している型やプロトコルのみ指定できます。以下のコードではエラーになるので、その時は associatedtype を使えばいいみたいです。

// error: Use of undefined associated type 'Counter'
protocol Foo: Sequence where Counter: Bar {
    associatedtype Counter
}

protocol Foo: Sequence {
    associatedtype Counter: Bar
}

SE-0143 Conditional conformances

型の拡張をする時に where 句を使って、ある条件下の時のみ拡張できるようになります。例えば Array の中身の ElementEquatable に準拠しているのであれば Array 自体も Equatable に準拠させるようにできます。

extension Array: Equatable where Element: Equatable {
  static func ==(lhs: Array<Element>, rhs: Array<Element>) -> Bool { ... }
}

※ 現状の Array でも比較演算子が使えるのは下記の関数のジェネリクスを使って実装されているからです。これよりも上記の条件付き拡張の方がどんな Array であれば比較演算子がつかえるのか分かりやすいです。

public func ==<Element : Equatable>(lhs: ContiguousArray<Element>, rhs: ContiguousArray<Element>) -> Bool

SE-0142と合わせてみると型チェックが動的に行えるような仕組みが提供されている気がします。今までは Type Erasure のような魔法が使われてきましたが、今後は意識しないでも自然に動的な型チェックができるようになるのかもしれないです。ここで今後のジェネリクスのマニフェストが公開されているので覗いてみてもいいかもです👏

14
6
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
14
6