Swiftのtypealiasは別名を与えるだけで型として区別されない、ということに注意して扱うべきという話

[inaka / Function Naming In Swift 3](http://inaka.net/blog/2016/09/16/function-naming-in-swift-3/) という記事、概ね良いこと書いてあると思いつつ、1つ賛同出来ないと思った点がありました。

こちらがそのコードと説明です:

```swift
typealias Path = String 

func request(for path: Path) -> URLRequest {  }
let request = request(for: "local:80/users")
```

> In this case, your parameter's type and its expected content are coherent and in harmony, because the parameter's type was made explicit through defining Path as a type alias for String.
This way, your function is still intuitive, readable, unambiguous, yet non-repetitive.

以下の違和感がありました。

- `path`というパラメーター名がすでにあるのに、さらにわざわざ`typealias`で`Path`という名前を用意していて冗長
  - 他にメリットがあるなら良いがわざわざこのためだけに`typealias`使うのは違和感
  - String型を受けるという情報を分かりにくくさせてしまっている
- Stringという単純な型に対して`typealias`で別名を与えているが、これは単なる別名であって、`String`と全く同じ扱いになる
  - `String`と`Path`は区別されない
  - `Path`とは何？と思ってコードを辿ると、ただの`String`かと分かるようになっていて、まどろっこしいだけ

> `String`と`Path`は区別されない

特にこの特性にも関わらず`String`という単純な型に別名を与えていることが問題だと思っています。
コード例のように文字列リテラルをそのまま与えられた場合にコンパイル通るのは良いとしても、以下のようにString型変数も与えてもコンパイル通ります。

```swift
let path: String = "local:80/users"
let r = request(for: path)
```

こういう使い方は一見良い感じに見えてしまうかもしれませんが、実際のメリットは無いと思っています。
Swiftの`typealias`が型を区別しない仕様に対しての不満ではなく、こういう単純な型に別名を与える用途には不適切、という意見です。

# Swiftの`typealias`の正しい使いどころ

上述の通り、単純な型に別名を与える目的では使うべきでは無いと思っています。
では、どういう時に使うかというと、以下の2点に絞られるはずです。

- 複雑な型に別名(簡略名)を与えたい時
    - タプル・クロージャー・階層の深いネストされた型など
- `protocol`で宣言された`associatedtype`に具体的な型を与える時

後者は完全に別の話になるので置いておいて、前者は以下のように同じ型を使い回したい時に定義を使い回すことが出来ます。

```swift
typealias PersonInfo = (firstname: String, lastname: String)
func foo(_ info: PersonInfo) {
    print("foo: \(info)")
}
func bar(_ info: PersonInfo) {
    print("bar: \(info)")
}
let info = ("First", "Last")
foo(info)
bar(info)
```

```swift
typealias MyClosure = (String) -> ()
func f1(_ closure: MyClosure) {
    closure("f1")
}
func f2(_ closure: MyClosure) {
    closure("f2")
}
f1 { print($0) }
f2 { print($0) }
```

`String`などの単純な型はそれをベタに書けば済み、`typealias`を使うと省力化するどころか、むしろ不便になると思っています。

> `Path`とは何？と思ってコードを辿ると、ただの`String`かと分かるようになっていて、まどろっこしいだけ

## 言語によっては、単純な型に別名を与える目的で使うのもあり (Go言語など)

例えば、Go言語は以下の挙動なので、typeで定義してそれを型として使うのはあり(よくやるパターン)だと思っています。

```go
type MyInt int

func test(i MyInt) {
    print(i)
}

func main() {
    anInt := 0
    test(anInt) // コンパイルエラー
    test(0) // OK
}
```

Goの場合、typeの名前で区別されるものとそうで無いものがあって、紛らわしいところもありますが…。
参考:
[Why can I type alias functions and use them without casting? - Stack Overflow](http://stackoverflow.com/questions/19334542/why-can-i-type-alias-functions-and-use-them-without-casting)
(Goの挙動は、 https://play.golang.org などで気軽に試せます。)

Swiftの`typealias`はいかなるものも名前で区別されません。

# では、型を区別したい時は？

基本的に`String`など単純な型をベタ書きで良いケースが多いと思っていますが、それでも型で区別したい時などもあると思います。
冒頭の、賛成出来ない例として出したコード例で説明します。

## 単純に`struct`など独自型を作る

ベタな方法ですが、まずは、これが良いと思っています。
冒頭の例を書き換えるとこうなります。

```swift
struct Path {
    let value: String
    init(value: String) {
        self.value = value
    }
}

func request(for path: Path) -> URLRequest {
    return URLRequest(url: URL(string: path.value)!)
}
let path = Path(value: "local:80/users")
let r = request(for: path) // request(for: "local:80/users") はコンパイルエラー
```

こうすると、`path`として想定していない他の`String`変数を誤って使ってしまうことを型で防げます。

`Path`が単純な`String`の別名では無く、ドメイン駆動設計(DDD)の値オブジェクトみたいな感じにもなりましたね。

以下、少し蛇足かもしれませんが、もう少し"便利"にしたい時のパターンです。

# Swiftの言語機能を活用して"便利"に

## `Protocol`を活用

`URLConvertible`という`Protocol`を宣言して、`Path`をそれに準拠させてみます。

```swift
protocol URLConvertible {
    func asUrl() -> URL
}

struct Path {
    let value: String
    init(value: String) {
        self.value = value
    }
}

extension Path: URLConvertible {
    func asUrl() -> URL {
        return URL(string: value)!
    }
}

func request(for url: URLConvertible) -> URLRequest {
    return URLRequest(url: url.asUrl())
}
let path = Path(value: "local:80/users")
let r = request(for: path)
```

上のようにすると、`URLConvertible`のみ引数として認めるようにすると、例えば`String`もそれに準拠させて、以下のように`String`を渡せるようにもなります。

```swift
extension String: URLConvertible {
    func asUrl() -> URL {
        return URL(string: self)!
    }
}

request(for: "local:80/users")
let path2 = "local:80/users"
request(for: path2)
```

元々の、「型で区別」というメリットから遠ざかってきた感じもしますが、例えば以下のように異なる`Protocol`として取り扱っている文字列を誤って利用してしまうミスは型で防げるので、シーンによっては活用出来ると思います。

```swift
protocol SomeProtocol {}
extension String: SomeProtocol {}
let path3: SomeProtocol = "local:80/users"
request(for: path3) // コンパイルエラー
```

このやり方は実際に[`Alamofire`などの実装にも取り入れられています](https://github.com/Alamofire/Alamofire/blob/master/Source/Alamofire.swift)。
(意図しない型を防ぐというより、オーバーロードぽいことをスマートに実現するためのテクニックとして使われている気がしますが。)

## `ExpressibleByXXXLiteral`を活用

上の`Protocol`を活用した例とは別のアプローチで、引数の型を区別しつつ`String`リテラルの利用を許可させてみます。
`String`リテラルを`Path`として受けられるように拡張します。

```swift
extension Path: ExpressibleByStringLiteral {
    init(stringLiteral value: StringLiteralType) {
        self.value = value
    }
    init(unicodeScalarLiteral value: String) {
        self.init(stringLiteral: value)
    }
    init(extendedGraphemeClusterLiteral value: String) {
        self.init(stringLiteral: value)
    }
}
```

そうすると、以下のように`String`リテラル直接使用した場合や、Path型を明示した場合は使えますが、`String`型として取り扱っている場合(変数`path`)はコンパイルエラーになります。

```swift
func request(for path: Path) -> URLRequest {
    return URLRequest(url: URL(string: path.value)!)
}
request(for: "local:80/users")
let path = "local:80/users"
request(for: path) // コンパイルエラー
let path2: Path = "local:80/users"
request(for: path2)
```
上に書いたGo言語で`type`を使った場合と似た挙動ですね。

---

最後の2つの例は利用シーンが限定されるとは思いますが、型の区別と取り扱いのしやすさを両立したい時の実装パターンでした。
(素直にベタに`struct`使った方が良いような、とは思いつつ。)