がばがばscala独学 - 型パラメータ(1)

# はじめに

下記のテキストを通して、基本的な書き方を学びつつ、個人的に気になったことを検証していくメモ代わりです。
https://dwango.github.io/scala_text/basic.html
本当にありがとう、dwango様・・・

# 型パラメータ

* ざっくりまとめ
    * 型パラメータを使用したクラス定義
        * `class className[T]` など `[]` 内に型パラメータを記載
    * 継承なく、同一の型（ `A` , `B` ）に対して
        * 非変: `A = B`
    * `A` が `B` を継承しているとき
        * 共変: `B = A`
        * 反変: `A = B`
            * 反変のメリットは不明・・・（要調査）


## クラス定義の文法

* 基本型

    ```scala
    class クラス名[型パラメータ1, 型パラメータ2, ..., 型パラメータN](コンストラクタ引数1 :コンストラクタ引数1の型, コンストラクタ引数2 :コンストラクタ引数2の型, ...) {
      0個以上のフィールドの定数またはメソッド定義
    }
    ```

* 実際の使用例

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    class Cell[A](var value: A) {
      def put(newValue: A): Unit = {
        value = newValue
      }
      def get(): A = value
    }

    val cell = new Cell[Int](1)
    println(cell.get())
    cell.put(2)
    println(cell.get())

    // Exiting paste mode, now interpreting.

    1
    2
    defined class Cell
    cell: Cell[Int] = Cell@bf0b927
    ```

* 型パラメータが効いて、エラーになるパターン

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    class Cell[A](var value: A) {
      def put(newValue: A): Unit = {
        value = newValue
      }
      def get(): A = value
    }

    val cell = new Cell[Int](1)
    cell.put("something")

    // Exiting paste mode, now interpreting.

    <console>:19: error: type mismatch;
     found   : String("something")
     required: Int
                  cell.put("something")
                           ^
    ```

### 実用的な例

* 基本型に忠実に作成した場合

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    class Pair[A, B](val a:A, val b:B) {
    override def toString(): String = "(" + a + "," + b + ")"
    }

    def divideFull(m: Int, n:Int): Pair[Int, Int] = new Pair[Int, Int](m/n, m%n)
    divideFull(7, 3)

    // Exiting paste mode, now interpreting.

    defined class Pair
    divideFull: (m: Int, n: Int)Pair[Int,Int]
    res4: Pair[Int,Int] = (2,1)
    ```

* 引数より推測できる場合

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    class Pair[A, B](val a:A, val b:B) {
    override def toString(): String = "(" + a + "," + b + ")"
    }

    def divide(m: Int, n:Int): Pair[Int, Int] = new Pair(m/n, m%n)
    divide(7, 3)

    // Exiting paste mode, now interpreting.

    defined class Pair
    divide: (m: Int, n: Int)Pair[Int,Int]
    res5: Pair[Int,Int] = (2,1)
    ```

* ちなみに、型指定じゃなくした場合

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    class Pair[A, B](val a:A, val b:B) {
    override def toString(): String = "(" + a + "," + b + ")"
    }

    def divide(m: Int, n:Int): Pair = new Pair[Int, Int](m/n, m%n)
    divide(7, 3)

    // Exiting paste mode, now interpreting.

    <console>:13: error: class Pair takes type parameters
                def divide(m: Int, n:Int): Pair = new Pair[Int, Int](m/n, m%n)
                                           ^
    ```
    * 型定義の場合は推測してくれない

## 変位指定（variance）

### 非変（invariant）

* 何も指定しなかった型パラメータは通常は非変（invariant）になる

    ```scala
    val : G[A] = G[B]
    ```
    * 上記ができる場合は、非変であり `A=B` である場合のみである

* 実例
  * 代入できない場合

        ```scala
        scala> val arr: Array[Any] = new Array[String](1)
        <console>:7: error: type mismatch;
        found   : Array[String]
        required: Array[Any]
        Note: String <: 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 arr: Array[Any] = new Array[String](1)
                                   ^
        ```

  * 代入できる場合

        ```scala
        scala> val arr: Array[String] = new Array[String](1)
        arr: Array[String] = Array(null)
        ```

### 共変（covariant）

* A が B を継承しているときにのみ代入が許される性質

    ```scala
    val : G[B] = G[A]
    ```

* 基本型

    ```scala
    class G[+A]
    ```
  
* 実例

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    class Pair[+A, +B](val a: A, val b: B) {
      override def toString(): String = "(" + a + "," + b + ")"
    }

    val pair: Pair[AnyRef, AnyRef] = new Pair[String, String]("foo", "bar")

    // Exiting paste mode, now interpreting.

    defined class Pair
    pair: Pair[AnyRef,AnyRef] = (foo,bar)
    ```
    * Pairは作成時に値を与えたら後は変更できず、したがってArrayStoreExceptionのような例外が発生する余地がない
    * 一般的には、一度作成したら変更できない（immutable）などの型パラメータは共変にしても多くの場合問題がない

### 反変（contravariant）

* A が B を継承しているときにのみ代入が許される性質

    ```scala
    val : G[A] = G[B]
    ```
  
  
* 基本型

    ```scala
    class G[-A]
    ```

* 実例

    ```scala
    scala> val x1: AnyRef => AnyRef = (x: String) => (x:AnyRef)
    <console>:7: error: type mismatch;
    found   : String => AnyRef
    required: AnyRef => AnyRef
         val x1: AnyRef => AnyRef = (x: String) => (x:AnyRef)
                                                ^

    scala> val x1: String => AnyRef = (x: AnyRef) => x
    x1: String => AnyRef = <function1>

    ```

* 単純に逆にしてみるとどうなる？

    ```scala
    scala> :paste
    // Entering paste mode (ctrl-D to finish)

    class Pair[-A, -B](val a: A, val b: B) {
    override def toString(): String = "(" + a + "," + b + ")"
    }

    val pair: Pair[String, String] = new Pair[AnyRef, AnyRef]("foo", "bar")

    // Exiting paste mode, now interpreting.

    <console>:7: error: contravariant type A occurs in covariant position in type => A of value a
         class Pair[-A, -B](val a: A, val b: B) {
                                ^
    <console>:7: error: contravariant type B occurs in covariant position in type => B of value b
         class Pair[-A, -B](val a: A, val b: B) {
                                          ^
    ```
    * ~~反変の使い所がわからないなー。。。~~
        * コメント参照