Custom Operator in Swift3


Swift3におけるカスタムオペレータについて気になったので調べてみました。


## 構成

検証を行うため、以下の構成を準備しました。

![160917-0002.png](https://qiita-image-store.s3.amazonaws.com/0/2014/145a7a70-6c43-07de-ac14-4c8e2c2a8ec4.png "160917-0002.png")

`OperatorFramework`は`Cocoa Touch Framework`であり内部に`Operator.swift`を持っています。`Operator.swift`内にてカスタムオペレータを定義しています。`Application`は`iOS Application`です。`Application`の内部で`import OperatorFramework`をすることでカスタムオペレータを使用するという構成です。

## 検証

1. 通常使用動作確認
2. `Application`側で`precedencegroup`を再定義する
3. `Application`側でカスタムオペレータ実装を書き換える
4. `OperatorFramework`側で使用しているオペレータを`Application`で再実装する
5. 同じオペレータを実装する別のFrameworkをimportする


## 通常使用動作確認

`Operator.swift`内の実装は以下のようにします。

```swift:Operator.swift
precedencegroup Base {
	associativity: left
	lowerThan: AdditionPrecedence
}

infix operator <+> : Base
infix operator <*> : Base

public func <+> (lhs: Int, rhs: Int) -> Int {
	print("<+>: called `Base`")
	
	return lhs + rhs
}

public func <*> (lhs: Int, rhs: Int) -> Int {
	print("<*>: called `Base`")
	
	return lhs * rhs
}
```

左結合にし、`AdditionPrecedence`よりも優先度を低くしています。`<+>`と`<*>`はそれぞれ2つの`Int`パラメータを取り、print logを吐き出しつつ和と積の結果を返します。

これを`Application`側で使用します。

```swift:ViewController.swift

import UIKit
import OperatorFramework

class ViewController: UIViewController {
	override func viewDidLoad() {
		super.viewDidLoad()
		
		print(" ")
		print(" -------------------------------- ")
		print(2 <+> 3 <*> 4)
		print(" -------------------------------- ")
		print(" ")
		
	}
}

```

ログはこんな感じに。

![160917-sysoutImage_01.png](https://qiita-image-store.s3.amazonaws.com/0/2014/2d8549aa-ecf4-e9e8-413d-7414032e5ae2.png "160917-sysoutImage_01.png")


左結合なので`<+>`である和が先に計算され、その後`<*>`である積が計算されます(20 = (2+3)*4)。

## `Application`側で`precedencegroup`を再定義する

では次に`Application`側で`precedencegroup`を再定義してみます。具体的には以下の通りに修正します。

```swift:ViewController.swift
class ViewController: UIViewController {
	override func viewDidLoad() {
		super.viewDidLoad()
		
		print(" ")
		print(" -------------------------------- ")
		print(2 <+> 3 <*> 4)
		print(" -------------------------------- ")
		print(" ")
		
	}
}

/// 以下を追加

precedencegroup Hacked {
	associativity: left
	higherThan: MultiplicationPrecedence
}

infix operator <*> : Hacked

```

すると出力がこう変わります。

![160917-sysoutImage_02.png](https://qiita-image-store.s3.amazonaws.com/0/2014/7b895672-ef3b-e47c-6392-10d52b3a86a9.png "160917-sysoutImage_02.png")

左結合であるのは変わらないのですが、優先度が`Hacked`>`Base`であるため`14=2+(3*4)`の順番に処理されます。つまり、`framework`側で複数のオペレータを実装している場合、 **意図しない順序で実行される可能性がある** ということです。

ちなみに、今回は`Hacked`という名前にしましたが、`Base`という同じ名前にしても問題なくビルドが通り、同様の挙動になります。prefix等は気にしないで良さそうです。

## `Application`側でカスタムオペレータ実装を書き換える

次に実装まで記述します。

```swift:ViewController.swift
class ViewController: UIViewController {
	override func viewDidLoad() {
		super.viewDidLoad()
		
		print(" ")
		print(" -------------------------------- ")
		print(2 <+> 3 <*> 4)
		print(" -------------------------------- ")
		print(" ")
		
	}
}

precedencegroup Hacked {
	associativity: left
	higherThan: MultiplicationPrecedence
}

infix operator <*> : Hacked

/// 以下を追加

func <*> (lhs: Int, rhs: Int) -> Int {
	print("<*>: called `Hacked`")
	
	return lhs
}

```

すると出力はこうなります。

![160917-sysoutImage_03.png](https://qiita-image-store.s3.amazonaws.com/0/2014/57831939-39f5-e8f2-d3a2-901588662cb0.png "160917-sysoutImage_03.png")

`Application`側で実装された`<*>`が呼ばれ、5=2+3と成っています。では、`<*>`を`OperatorFramework`側で使用していた場合はどうなるのでしょうか。

## `OperatorFramework`側で使用しているオペレータを`Application`で再実装する

`Application`側はそのままに`OperatorFramework`側を以下のように書き換えます。

```swift:Operator.swift
precedencegroup Base {
	associativity: left
	lowerThan: AdditionPrecedence
}

infix operator <+> : Base
infix operator <*> : Base

public func <+> (lhs: Int, rhs: Int) -> Int {
	print("<+>: called `Base`")
	
	//return lhs + rhs
	return lhs <*> rhs  // <- New
}

public func <*> (lhs: Int, rhs: Int) -> Int {
	print("<*>: called `Base`")
	
	return lhs * rhs
}
```

すると出力は以下のようになります。

![160917-sysoutImage_04.png](https://qiita-image-store.s3.amazonaws.com/0/2014/52b586ef-e405-619d-bc47-0c50da96577d.png "160917-sysoutImage_04.png")

`OperatorFramework.<+>`内で使用している`<*>`は`OperatorFramework`のものを呼び出しています。

## 同じオペレータを実装する別のFrameworkをimportする

新たにFramework(ここでは`OtherFramework`とする)を定義し、その中で`<+>`と`<*>`を実装します。その状態で`Application`側で`import OtherFramework`するとビルドNGとなります。ただし、`Application`側で再実装するとビルドOKとなります。

```swift:ViewController.swift
import UIKit
import OperatorFramework
import OtherFramework

class ViewController: UIViewController {
	override func viewDidLoad() {
		super.viewDidLoad()
		
		print(" ")
		print(" -------------------------------- ")
		print(2 <+> 3 <*> 4)
		print(" -------------------------------- ")
		print(" ")
		
	}
}

precedencegroup Hacked {
	associativity: left
	higherThan: MultiplicationPrecedence
}

infix operator <*> : Hacked
infix operator <+> : Hacked

// 以下がないと`OperatorFramework`と`OtherFramework`の実装が衝突してビルドNG

func <*> (lhs: Int, rhs: Int) -> Int {
	print("<*>: called `Hacked`")
	
	return lhs
}

func <+> (lhs: Int, rhs: Int) -> Int {
	print("<+>: called `Hacked`")
	
	return lhs
}
```

## まとめ

- importする側で同じ演算子を再定義すると優先度が変化する
- 同じオペレータを定義している2つのframworkをimportしようとするとビルドNG

部分的にimportするか部分的にimportしない仕組みがないうちは、カスタムオペレータをframwork側が提供するのは避けた方がいいのかもしれません。