Posted at

Swiftに今後追加される言語仕様について調べてみた

More than 3 years have passed since last update.

mixiグループアドベントカレンダー25日目です!


はじめに

モバイルグループの田代です!

最近新規画面はSwiftで書いてます!

そのおりにSwift3.0の話がチーム内ででたので、そのあたりを調べてみました!


概要

Swiftがgithubで公開されAppleだけでなく開発者もSwiftの言語仕様にコミットできるようになりました

それがSwift-Evolutionです!

現在(2015/12/24)でも提案された8つの新仕様がAcceptedとなりSwift2.2やSwift3.0で導入する予定となっています

今回はどういった仕様が追加されていくのか見ていきたいと思います


Proposal


Proposalとは

新仕様の提案書です

上記リポジトリに0000-template.mdというフォーマットが用意されており、それを元に提案したい仕様をまとめてコミットしレビューしてもらい、導入するか否かを決めてもらいます


Proposals


SE-0001: Allow (most) keywords as argument labels (Accepted)


概要

inout,var,let を除いた単語を引数ラベルとして設定できるようにする


Xcode7.2

func hoge(in:String) {   /// Error:Expected ',' separator

}

たとえば上記のように in を仮引数のラベルに設定するとXcode7.2ではエラーになります

ただこれはバグらしく、修正コミットがこちらにあります

https://github.com/apple/swift/commit/c8dd8d066132683aa32c2a5740b291d057937367

こうやって変更点をコードで確認できるのはうれしいですね


SE-0002: Removing currying func declaration syntax (Accepted)


概要

Swiftにはカリー化のための専用の宣言方法がある(初めて知った!)のですが、わかりづらいから削除しようとのことらしいです


Xcode7.2

// Before:

func curried(x: Int)(y: String) -> Float {
return Float(x) + Float(y)!
}

// After:
func curried(x: Int) -> (String) -> Float {
return {(y: String) -> Float in
return Float(x) + Float(y)!
}
}


上記のProposalの例はどちらもXcode7.2では動きますが今後はBeforeはなくなるようです


SE-0003: Removing var from Function Parameters and Pattern Matching (Accepted)


概要

関数パラメーターとパターンマッチングからvarを削除するとのことですが、よくわからないので例文を見ていきます

例示は理解の試金石ですね


Xcode7.2

func foo(i: Int) {

i += 1 // illegal
}

func foo(var i: Int) {
i += 1 // OK
}


上記の例文ではiがimmutableになっているのに再代入しようとしてエラーになってしまいます

Proposalでは他にもif,while,guard,switch,forなどの例があり、どれも変数をimmutableに宣言することによってその後に変数を変更しようとするとエラーになってしまうことが列挙されています

パターンマッチングの例も書いてあったのですが、なにがパターンマッチングなのか判然としなかったので詳しい人がいたら教えてほしいですm(_ _)m

上記solutionとして提示されているのは


  • All function parameters are either unannotated constants or are marked with inout.

  • Only if let is allowed, not if var.

  • Only guard let is allowed, not guard var.

  • Only while let is allowed, not while var.

  • Only case .Some(let x) is allowed, not case .Some(var x).

  • Only for x in is allowed, not for var x in.

となっており、基本的にimmutableとして扱い、mutableにしたい場合は

if let x = getOptionalInt() {

var x = x
x += 1
return x
}

と一度置き換えるようです


SE-0004: Remove the ++ and -- operators (Accepted)


概要

簡単にいえば++--のオペレーターを廃止しようということです

理由としてはx++++xで値の返り値が違うけどあんまり気にしてないよね?ということらしいです

var i=0

i += 1 /// 1

ではどうやってincrementやdecrementを表現するのかといえば上記のようにするようです

ただ代替案も提示されていてx++++xはどちらも同じ結果が変えるようにするということですが、

最終的にどうなるかは気になるところです

ちなみにPythonには++--のオペレーターはないそうです


SE-0005: Better Translation of Objective-C APIs Into Swift (Accepted)


概要

Swift内でのObjective-CのAPIをよりよく置き換えるとのことですが、これは例を見たほうがわかりやすいと思うのでPoposalの例を見てみます


swift2.0

class UIBezierPath : NSObject, NSCopying, NSCoding {

convenience init(ovalInRect: CGRect)
func moveToPoint(_: CGPoint)
func addLineToPoint(_: CGPoint)
func addCurveToPoint(_: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint)
func addQuadCurveToPoint(_: CGPoint, controlPoint: CGPoint)
func appendPath(_: UIBezierPath)
func bezierPathByReversingPath() -> UIBezierPath
func applyTransform(_: CGAffineTransform)
var empty: Bool { get }
func containsPoint(_: CGPoint) -> Bool
func fillWithBlendMode(_: CGBlendMode, alpha: CGFloat)
func strokeWithBlendMode(_: CGBlendMode, alpha: CGFloat)
func copyWithZone(_: NSZone) -> AnyObject
func encodeWithCoder(_: NSCoder)
}


swift3.0

class UIBezierPath : Object, Copying, Coding {

convenience init(ovalIn: CGRect)
func moveTo(_: CGPoint)
func addLineTo(_: CGPoint)
func addCurveTo(_: CGPoint, controlPoint1: CGPoint, controlPoint2: CGPoint)
func addQuadCurveTo(_: CGPoint, controlPoint: CGPoint)
func append(_: UIBezierPath)
func reversing() -> UIBezierPath
func apply(_: CGAffineTransform)
var isEmpty: Bool { get }
func contains(_: CGPoint) -> Bool
func fillWith(_: CGBlendMode, alpha: CGFloat)
func strokeWith(_: CGBlendMode, alpha: CGFloat)
func copy(zone _: Zone = nil) -> AnyObject
func encodeWith(_: Coder)
}

要点としては


swift_name属性の適用を一般化する

swift_name の存在をいま知ったわけですが、Objective-CにSwiftのための名前を定義できる仕組みのようです

// @see https://github.com/apple/swift/blob/master/test/ClangModules/attr-swift_name.swift

// CHECK: warning: too few parameters in swift_name attribute (expected 2; got 1)
// CHECK: + (instancetype)testW:(id)x out:(id *)outObject SWIFT_NAME(ww(_:));

いまはenumやファクトリーメソッドのリネームにしか許可されてないけどCやObjective-Cの実体(entity)?の任意のリネームもできるようにしようということらしいです


冗長な型名の切り落とし

SwiftAPIのガイドラインに合わせて、不要な単語を省略しよう

public mutating func removeElement(member: Element) -> Element? /// bad

public mutating func remove(member: Element) -> Element? /// good


デフォルト引数を追加する

SwiftにAPIをインポートするとき、デフォルト引数に必要なヒントあれば推論してくれるようにする


一番初めの引数にラベルを追加する

メソッドの最初のパラメーターがデフォルト値が設定されていたら、そのメソッドのためにラベルを設定しようとのことです

https://swift.org/documentation/api-design-guidelines.html#first-argument-label


ブーリアン型のプロパティにはisを前につける

Objective-Cのガイドラインではisを接頭語としてつけるのは禁じられていましたが、Swiftのガイドラインに合わせようということらしいです


FoundationAPIsのNSプレフィックスは除去する

クロスプラットフォームAPIにプレフィックスは時代錯誤っぽいのでやめようって話みたいです

詳細はProposalに書いてあるのでそちらを見てほしいのですが、

これは最終的には開発者にも影響あるのですが、まずはApple内でのObjective-Cの規約のような感じもします


SE-0006: Apply API Guidelines to the Standard Library (Awaiting Review)


概要

これはSwift3.0向けのガイドラインをまとめようぜという話らしく、ひとまずアプリ開発者には関係なさそうです


SE-0007: Remove C-style for-loops with conditions and incrementers (Accepted for Swift 3.0)


概要

Cライクなfor-loopを削除しようとのことです

理由としてはfor-instrideがSwiftらしい同じような価値を提供してくれるし、for-inのほうがfor-loopsに比べて文字数が少ないし、SE-0004でもあったけど++などのoperator削除されるから、などの理由が挙げられてました

for (var i=0; i<10; i++) {

//-- anything
}

ちなみにC-style for-loopは上記みたいな構文です


SE-0008: Add a Lazy flatMap for Sequences of Optionals (Accepted for Swift 2.2)


概要

現在flatmapには二種類あって(詳しくはkoherさんの記事をどうぞ)Optionalをとるものとそうでないものがあります

それがそれぞれで.lazyしたときの挙動が違うので治そうということみたいです


Xcode7.2

[1, 2, 3]

.lazy
.flatMap { n in n..<5 }
// LazyCollection<FlattenBidirectionalCollection<LazyMapCollection<Array<Int>, Range<Int>>>>

(1...10)
.lazy
.flatMap { n in n % 2 == 0 ? n/2 : nil }
// [1, 2, 3, 4, 5]



終わりに

ざっとReview済みのProposalを見てきましたが、既存のObjective-C周りにも言及されていて、SwiftのNSRangeまわりではまった自分としてはそのあたりも使いやすくなってくれるとうれしいです