型パラメータと変位指定について調べたことをつらつら書いてく

scalaで型パラメータや変位指定について色々調べていくうちに学んだことや面白いと思ったことをつらつら書き残しておこうと思います。

(＊) 私は言語に特別詳しいわけでもないので「こうじゃないかな？」といった憶測を多分に含んでいます。

# ちょっとその前に

### 継承と派生型

ぶっちゃけ継承や派生型を語るには知識が足りなさすぎるのですが、それは置いておいて今回大事になるのは以下のような関係です。

```scala
scala> val i: Int = 123
i: Int = 123

scala> val a: Any = i
a: Any = 123
```

`Int`型を`Any`型に代入しています。`Int`は`Any`（正確には`Any`を継承している`AnyVal`）を継承しているので、基本型である`Any`型の変数に代入が可能です。これは基底クラスである`Any`の機能を派生クラスである`Int`が全て引き継いでいる（というか強制している）ため可能なのだと思われます。つまり、`Int`を`Any`として扱っても問題ないということでしょう。また`Int`が`Any`の派生であるということは以下のような包含関係をイメージできるかと思います。

<img src="https://qiita-image-store.s3.amazonaws.com/0/277414/83972965-7e55-a2e4-8ebe-71f3f95b45e1.png" width=300 />

### 余談 その1

これを機会に継承や派生型をもう一度ちゃんと調べて理解しておこう！と思ったは良いのですが、これらの話の歴史・闇・沼が深すぎてびっくりです。いやー、プログラミング言語って凄いですね。

# 型パラメータ

では早速型パラメータについてです。githubでライブラリを調べるとしょっちゅう出てきますね。Javaやswiftではジェネリクス（総称型）と呼ばれてますね。ここでは仮に`MyContainer`を定義していきたいと思います。

```scala
class MyContainer[A](val content: A)

val intContainer = new MyContainer(1)
val stringContainer = new MyContainer("string")
```

どんな型でも入れられるガバガバ状態ですね。ではここで`MyContainer`で扱うための`BaseContent`というクラスと、その派生クラスである`DerivedContent`を用意したいと思います。

```scala
class BaseContent
class DerivedContent extends BaseContent
```

何ともまぁ手抜きなクラスですが、こんな手抜きも許してくれるscalaコンパイラに感謝ですね。この`BaseContent`と`DerivedContent`には継承関係があるので以下のようなイメージになるかと思います。

<img src="https://qiita-image-store.s3.amazonaws.com/0/277414/b9757ad6-8f1b-446f-ee80-33617b5996c5.png" width=300 />

ではこの`DerivedContent`を型パラメータとしてとる`MyContainer[DerivedContent]`は`MyContainer[BaseContent]`の派生型として扱えるでしょうか。答えは「扱えない」です。

```scala
val content1 = new DerivedContent()
val container1 = new MyContainer(content1)
val container2: MyContainer[BaseContent] = container1 // compile error
```

ですが、scalaのコンパイラはとても賢いことに「君のやりたいことはもしかして〜」と提案してくれます。そのエラー内容が以下です。

```
error: type mismatch;
 found   : this.MyContainer[this.DerivedContent]
 required: this.MyContainer[this.BaseContent]
Note: this.DerivedContent <: this.BaseContent, but class MyContainer is invariant in type A.
You may wish to define A as +A instead. (SLS 4.5)
val container2: MyContainer[BaseContent] = container1
                                           ^
one error found
```

「`MyContainer`は型Aにおいて`invariant`だよ」と教えてくれてます。この`invariant`が日本語で言う「不変（または非変）」らしいですね。

# 変位指定

ではコンパイラに怒られた不変とは何か？ですが、型パラメータの性質の1つみたいですね。型パラメータの性質として以下のようなものがあるそうです。

* 共変（covariant）：型パラメータの派生型（または同一型）を許容。`+A`のように定義する。
* 反変（contravariant）：型パラメータの基本型（または同一型）を許容。`-A`のように定義する。
* 不変（invariant）：型パラメータの同一型のみ許容。`A`のように何も指定せずに定義する。

これまでの`MyContainer[A]`は不変だったことが分かりますね。ゆえに`MyContainer[BaseContent]`型への`MyContainer[DerivedContent]`型の代入はエラーだったんでしょう。また、上記より`MyContainer`の型パラメータ`A`は共変性を持つように`+A`という指定をすべきことが分かります。（というかコンパイラもそう言ってますね、本当に賢いやつです。）

### 余談 その2

「共変 反変」でググると最初にベクトルのお話が出てきます。ゆえに一時期まで私は勝手に「共変と反変はベクトル由来の概念なのかな？」と思っていました（内緒です）。ですが[wikipedia](https://ja.wikipedia.org/wiki/%E5%85%B1%E5%A4%89%E6%80%A7%E3%81%A8%E5%8F%8D%E5%A4%89%E6%80%A7_(%E8%A8%88%E7%AE%97%E6%A9%9F%E7%A7%91%E5%AD%A6))によると、これまた圏論由来とありびっくり。さすが圏論、本当どこにでも出てきますね〜。（そもそも圏論の共変と反変自体がベクトルの話由来なのかもしれませんけどね）

## 共変

では先程のコンパイラ先生の言う通りに変更して共変性なるものを試してみたいと思います。

```scala
class MyContainer[+A](val content: A)

class BaseContent
class DerivedContent extends BaseContent

val content1 = new DerivedContent()
val container1 = new MyContainer(content1)
val container2: MyContainer[BaseContent] = container1
```

コンパイル通りました！これで`OreOreList`が作れるぜ！

ですがなぜわざわざ`+A`などという変位指定を加えなければならないのでしょうか。「全部共変でいいじゃん」と思う方もいらっしゃるかと思います。私も最初は謎だったのですが、調べていくとJavaの配列が分かりやすい例として挙げられていました。

```java
public class Test {
  public static void main(String args[]){
    String[] strList = {"string"};
    Object[] objList = strList;
    objList[0] = 1;
  }
}
```

こちらのJavaの簡単な例ですが、コンパイルは通りますが実行時エラーになります。それもそのはず、`String[]`型の要素に`Integer`型の値を入れているのですから。これはそもそもJavaの配列がミュータブルにも関わらず共変であるためだそうです。このような事態を防ぐためにも「ミュータブルな型は非変でなければならい」という設計にしなければならないそうです。逆に、イミュータブルであれば（参照のみなので）共変でも大丈夫だそうです。ゆえに、scalaではイミュータブルな`List`は共変、ミュータブルな`Array`は非変となっているそうですね。

```scala
scala> val list1 = List(1, 2, 3)
list1: List[Int] = List(1, 2, 3)

scala> val list2: List[Any] = list1
list2: List[Any] = List(1, 2, 3)

scala> val array1 = Array(1, 2, 3)
array1: Array[Int] = Array(1, 2, 3)

scala> val array2: Array[Any] = array1
<console>:12: error: type mismatch;
 found   : Array[Int]
 required: Array[Any]
Note: Int <: Any, but class Array is invariant in type T.
You may wish to investigate a wildcard type such as `_ <: Any`. (SLS 3.2.10)
       val array2: Array[Any] = array1
                                ^
```

### 余談 その3

「ミュータブルな型は不変でなければ〜」みたいに書くととてもややこしいですねw　「それはミュータブルなの！？イミュータブルなの！？」とつっこまれそうです。immutableもinvariantも日本語だと「不変」と訳すそうですが、invariantが「非変」とも呼ばれているのはこういった曖昧さを回避するためなんですかね？

あ、あとJavaを悪い例みたいに挙げてしまいましたが、決してJavaをディスりたいわけではないです（念の為）。viva らぶあんどぴーす。

## 反変

初めて変位指定を知ったとき、正直「共変はまだしも反変は何の役に立つんだ？」と思ってました。そういう時期が、私にもありました。ですが有名な[某コップ本](https://www.amazon.co.jp/Scala%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%A9%E3%83%96%E3%83%AB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E7%AC%AC3%E7%89%88-Martin-Odersky/dp/4844381490/ref=sr_1_5?ie=UTF8&qid=1553159484&sr=8-5&keywords=scala+%E5%85%A5%E9%96%80)にもあるように、関数の定義において知らず知らず使われていたんですね。では実際にscalaの関数の定義を見てみたいと思います。

```scala
trait Function1[-T1, +R] extends AnyRef {
  def apply(v1: T1): R
}
```

[実際の定義](https://github.com/scala/scala/blob/8729d71ee722e6e1293fec5ded5db22114ec0a16/src/library/scala/Function1.scala#L65)から少々、いやかなーり勝手に省略した形ですが、引数を1つとる関数の定義です。大事なのは`Function1[-T1, +R]`の`-T1`および`+R`です。引数の型（`-T1`）が反変、返り値の型（`+R`）が共変となっています。これは関数の派生型の一般的なルールとして以下が成り立つためだそうです。以下の x, x', y, y' は型を、x' ⊆ x は x' が x の派生型であることを、x → y は引数の型が x 返り値の型が y の関数を意味しています。（たしか何かの論文からの引用なのですがオレオレメモに式しか残ってない・・・）

```math
x' \subseteq x, y \subseteq y' \Rightarrow x \rightarrow y \subseteq x' \rightarrow y'
```

まんま`Function1`の定義と同じではないでしょうか。以下のように実例を手元で試すのが分かりやすいと思います。

```scala
scala> val func1: Any => Int = x => 1
func1: Any => Int = $$Lambda$1108/1312250810@ab2e6d2

scala> val func2: Int => Int = func1
func2: Int => Int = $$Lambda$1108/1312250810@ab2e6d2

scala> val func3: Int => Any = func2
func3: Int => Any = $$Lambda$1108/1312250810@ab2e6d2
```

（引数をガン無視するので）引数を取る意味が全くないというセンスの欠片もない`func1`ですが、`func2`および`func3`に代入できてしまいます。これは`func1`が`func2`（および`func3`）の派生型であるからです。つまり、`func1 ⊆ func2 ⊆ func3`であり、以下のようにイメージして良いと思います。

<img src="https://qiita-image-store.s3.amazonaws.com/0/277414/2ac42e36-470f-a7b7-5d9f-08a68bdeb858.png" width=300 />

また基本型（`func3`）を派生型（`func1`）で置換しても問題ないということから、scalaの関数（の型定義）はリスコフの置換原則を満たしている、ということになるのではないでしょうか（より正確にはscalaを作っている方々がリスコフの置換原則を満たすように関数の型を定義している、とかでしょうか？）。「反変とか何の役に立つんだ？」とか思ってたくせにガッツリお世話になってましたね。（むしろ反変ってこんなにデキるやつだったんですね）

### 補足

もしかしたら（いやもしかしなくとも）関数の置換部分が分かりにくいかもしれないので、念の為補足しておきます。以降では`func1`と`func3`のみ考えます。まず引数について見たいと思います。

```scala
scala> func3(1)
res2: Any = 1

scala> func3("a")
<console>:13: error: type mismatch;
 found   : String("a")
 required: Int
       func3("a")
             ^

scala> func1(1)
res4: Int = 1

scala> func1("a")
res5: Int = 1
```

`func3`の引数の型は`Int`、`func1`の引数の型は`Any`です。`func1`を`func3`として扱う場合、`func1`には`Int`が入ると思われますが、`func1`の引数の型は`Any`であるため問題ありません。次に、返り値も見てみたいと思います。

```scala
scala> val a1: Any = func1(2)
a1: Any = 1

scala> val a2: Any = func3(2)
a2: Any = 1
```

`func3`の返り値は`Any`、`func1`の返り値の型は`Int`です。`func1`を`func3`として扱っても、`Any`を返す想定なので`Int`を返しても問題ありません。ここは「ちょっとその前に」の部分と同じような挙動ですね。

あとは視覚的にイメージするとしたら、ざっくりこんな感じになるんではないでしょうか？

<img src="https://qiita-image-store.s3.amazonaws.com/0/277414/b33447fe-7ed2-c7d3-e15b-d748eb1464a9.png" width="300" />


### 余談 その4

なんか反変というよりは関数にフォーカスした感じになってしまった気がします。。。でも反変も関数も調べれば調べるほど「こいつ・・・デキる！」と思えてしまったんで仕方がないと思うんです。

# 型境界

最後は型境界についてです。型境界に関しましても共変・反変よろしく「上限境界」と「下限境界」があるようですね。

* 上限境界（upper bounds）：`T <: Type`のように定義し、型パラメータ（`T`）は`Type`の派生型（または同一型）を許容する。
* 下限境界（lower bounds）：`T >: Type`のように定義し、型パラメータ（`T`）は`Type`の基本型（または同一型）を許容する。

## 上限境界

上限境界の「`Type`の派生型（または同一型）を許容する」ということは「少なくとも`Type`である」と言えるのではないでしょうか。そのため、少なくとも`Type`の持つメンバーにはアクセスできるはずです。以下は`Task`という型を上限境界とした場合の例です。

```scala
trait Task {
  def execute()
}

class EchoTask(val message: String) extends Task {
  def execute() = println(message)
}

class Executer[T <: Task](val task: T) {
  def execute() = task.execute()
}

val task = new EchoTask("message")
val executer = new Executer(task)
executer.execute()
```

また、上限境界では「少なくともXXXを実装している」ということを保証してくれるので、数学ちっくに「Aを満たしているのであればBが成り立つ」といった定理のような実装ができるのが超カッコいいですね。よく上限境界の例として出てくる`Ordered[T]`とかは「比較可能であればソートが可能」ということを表現しているのではないでしょうか。世の中の天才は凄いですね。

### 余談 その5

scalaでは`Ordered`の他に`Ordering`がありますね。こちらは型クラス（`implicit parameter`）による実装みたいです。

## 下限境界

では最後に下限境界ですが、これまた反変と一緒で「こいつは一体なんなんだ・・・」って感じでした。しかし共変と反変を学んでいくうちに下限境界の有用性というか大事さが少し分かりました。よく書籍において「共変・反変」の次に「上限境界・下限境界」という順で記載されているのはそのためなんですかね？

話が少し逸れてしまいましたが、下限境界を学ぶためにここでは`Option`を模倣して`MyOption`なるものを実装してみたいと思います。`MyOption`の機能として`isEmpty`、`get`、`getOrElse`を実装したいと思います。

```scala
abstract class MyOption[+A] {
  def isEmpty: Boolean
  def get: A
  def getOrElse(given: A): A = if (isEmpty) given else get // compile error
}
```

さっそく怒られました。

```
error: covariant type A occurs in contravariant position in type A of value given
  def getOrElse(given: A): A = if (isEmpty) given else get
                ^
one error found
```

「共変な型`A`が反変なポジションにあるよ」って感じでしょうか？これを何とかしてくれるのが「下限境界」みたいです。早速`getOrElse`を修正します。

```scala
abstract class MyOption[+A] {
  def isEmpty: Boolean
  def get: A
  def getOrElse[B >: A](given: B): B = if (isEmpty) given else get
}
```

何とかコンパイルできました。めでたしめでたし・・・ではあるのですが、このままではさっぱりです。「なぜ？」なのかが全く分かりません。ですがこのまま抽象クラスのみでは少々扱い辛いので、`Some`に相当する`MySome`を実装したいと思います。

```scala
class MySome[+A](val content: A) extends MyOption[A] {
  def isEmpty: Boolean = false
  def get: A = content
}
```

次にこの`MySome`を使って基本型である`MyOption[Any]`の変数に`MyOption[Int]`（正確には`MySome[Int]`）型の変数を代入したいと思います。

```scala
val option1: MyOption[Int] = new MySome(123)
val option2: MyOption[Any] = option1
println(option2.getOrElse("string"))
```

問題ありませんでした。ですが恐らくではありますが、ここで`getOrElse`の型が`def getOrElse(given: A): A`だった場合おかしなことになってるはずです。なぜなら`MyOption[Int]`の`getOrElse`の引数の型は`Int`であるのに、`MyOption[Any]`の引数の型が`Any`だからです。つまり、`Any => Any`であるはずなのに、実際は`Int => Int`だからです。（必要ないかもですが）視覚的にイメージするとしたら以下のような感じになるかと思います。

<img src="https://qiita-image-store.s3.amazonaws.com/0/277414/6e2d5bb4-3bdf-74d3-771c-da85ad4a56d5.png" width=300 />

コンパイルが通らないのであり得ないですが、仮にできたとしたら`class SomeInt extends MyOption[Int]`というクラスを作って`getOrElse`をオーバーライドする際に引数をインクリメントした値を返すという実装をし、上記のように`MyOption[Any]`型の変数に代入して`getOrElse("String")`を呼び出せば実行時エラーになりそうな気がしますね。

というわけで、共変な型パラメータを用いて関数の引数の型を定義したい場合は、下限境界で型パラメータの基本型を許容する必要があるんですね。

### 余談 その6

ここではscala標準ライブラリの[Option](https://github.com/scala/scala/blob/2.13.x/src/library/scala/Option.scala)をガッツリ参考にさせていただきました。githubにあげられてるオープンソースのプログラムはとても勉強になりますね。しかし、こういった良く言えば「参考」悪く言えば「パクり」もっと言えば「謎の改悪」のようなものを意気揚々とネットに公開するのって良いのかな・・・？出典とか引用はなるべくちゃんと書いてるつもりではあるのですが、正直どこまで許されてどこから許されないのかがよく分かっていないです・・・。

# 感想

調べれば調べるほど面白いですね。そろそろ年齢的にも勉強したことを片っ端から忘れていくかもしれないので備忘録として書き残しましたが、「なぜ」かを考えて書くとどうしても説明口調になってしまいましたね・・・。また、「こうじゃないかな？」という部分も書き残しましたが、実は間違っていたり、副次的なものであったり、はたまた因果が逆転していたりしないか心配です。まぁ、一番心配なのはただのこじつけではないか？ですけどね！もし野生のプロの方で「ここは違うぜ！」とか「本当はこうだ！」とかあった場合は、指摘していただけると幸いです。

# 参考資料

* [scala標準ライブラリ](https://github.com/scala/scala)
    * コミッターの方々には感謝しかありませんね。
* [TOUR OF SCALA](https://docs.scala-lang.org/tour/tour-of-scala.html)
    * 分野毎にシンプルにまとまってたり、サンプルがあったりで非常に助かりました。
* [ドワンゴさんの研修資料](https://dwango.github.io/scala_text/)
    * 私もこの研修受けたいです。純粋にうらやましいw
* [コップ本](https://www.amazon.co.jp/Scala%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%A9%E3%83%96%E3%83%AB%E3%83%97%E3%83%AD%E3%82%B0%E3%83%A9%E3%83%9F%E3%83%B3%E3%82%B0%E7%AC%AC3%E7%89%88-Martin-Odersky/dp/4844381490/ref=sr_1_5?ie=UTF8&qid=1553159484&sr=8-5&keywords=scala+%E5%85%A5%E9%96%80)
    * 会社で買ったりもしましたが、誰かが読んでるときに読めないのが嫌で自分用に買っちゃいました。

他にもたくさん参考にさせてもらってるんですが、さすがに書ききれないのでお許しください。