Scala
memo

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

はじめに

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

型パラメータ

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

クラス定義の文法

  • 基本型

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

    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> :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> :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> :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> :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)になる

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

    • 代入できない場合

      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> val arr: Array[String] = new Array[String](1)
      arr: Array[String] = Array(null)
      

共変(covariant)

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

    val : G[B] = G[A]
    
  • 基本型

    class G[+A]
    
  • 実例

    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 を継承しているときにのみ代入が許される性質

    val : G[A] = G[B]
    
  • 基本型

    class G[-A]
    
  • 実例

    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> :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) {
                                          ^
    
    • 反変の使い所がわからないなー。。。
      • コメント参照