Scala：ユーザ定義の依存型のクラス名（type別名）を使って、型レベルのパターンマッチを試みたらすべて失敗した件


###（ 後記 ）
以下の記事に、「ユーザ定義の依存型のクラス名（type別名）を使って、型レベルのパターンマッチ」を行う方法を解節しています。

https://qiita.com/electronics_diy721/items/ca5d376796c13c5f0986

このコードが実行エラーになる。

Refinedで定義した値制約に、__*type*キーワード__で__別名*ConditionedInt*__を付けた後、

>```Scala:sbtConsole
>scala> type ConditionedInt = Int Refined Interval.ClosedOpen[10000, 10000000]
>type ConditionedInt
>
>scala>
>```

case節のマッチング対象に、Stringと並んで、__*ConditionedInt*__を指定すると、認識されない。

>```Scala:sbtConsole
>scala> def class_check(a: Any) = {
>     |   a match {
>    （省略）
>     |     case _: ConditionedInt => "有料顧客の課金額"
>
>```

__（ エラー表示 ）__

>```Scala:sbtConsole
           case _: ConditionedInt => "有料顧客の課金額"
                   ^
On line 4: warning: non-variable type argument Int in type pattern eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Interval.ClosedOpen[10000,10000000]] (the underlying of ConditionedInt) is unchecked since it is eliminated by erasure
>```


###sbt consleでREPLを立ち上げて、ライブラリをインストールする

- 以下の__*build.sbt*ファイル__で、Scalaの依存型ライブラリ__*refined*__を読み込みます。

```text:build.sbt
scalaVersion := "2.13.6"
libraryDependencies += "eu.timepit" %% "refined" % "0.9.27"
```

- sbt consoleコマンドでREPL環境を立ち上げる。

```Scala:sbtConsole
% sbt console

（ 省略 ）

scala> import eu.timepit.refined._
import eu.timepit.refined._

scala> import eu.timepit.refined.auto._
import eu.timepit.refined.auto._

scala> import eu.timepit.refined.numeric._
import eu.timepit.refined.numeric._

scala> import eu.timepit.refined.api.{RefType, Refined}
import eu.timepit.refined.api.{RefType, Refined}

scala> import eu.timepit.refined.boolean._
import eu.timepit.refined.boolean._

scala> import eu.timepit.refined.char._
import eu.timepit.refined.char._

scala> import eu.timepit.refined.collection._
import eu.timepit.refined.collection._

scala> import eu.timepit.refined.generic._
import eu.timepit.refined.generic._

scala> import eu.timepit.refined.string._
import eu.timepit.refined.string._

scala> import shapeless.{ ::, HNil }
import shapeless.{$colon$colon, HNil}

```

- 今回は、(名前:String, 数字：Int）の要素数２のTupleを扱います。

```Scala:sbtConsole
scala> val john_tuple = ("John", 100000)
val john_tuple: (String, Int) = (John,100000)

scala> val ken_tuple = ("John", 20)
val ken_tuple: (String, Int) = (John,20)

scala> var (a, b) = john_tuple
var a: String = John
var b: Int = 100000

scala> a
val res2: String = John

scala> b
val res3: Int = 100000

```

- 値範囲 [10000, 10000000]を持ちうる整数Intのデータ型を、__*ConditionedInt*型__と名付ける。
- データのデータ型によって、処理を分けるcase文に、__*ConditionedInt*型__を組み入れる。
- エラーが吐かれた。

```Scala:sbtConsole
scala> type ConditionedInt = Int Refined Interval.ClosedOpen[10000, 10000000]
type ConditionedInt

scala> def class_check(a: Any) = {
     |   a match {
     |     case _: String => "文字列"
     |     case _: ConditionedInt => "有料顧客の課金額"
     |     case _ => "不明"
     |   }
     | }
           case _: ConditionedInt => "有料顧客の課金額"
                   ^
On line 4: warning: non-variable type argument Int in type pattern eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Interval.ClosedOpen[10000,10000000]] (the underlying of ConditionedInt) is unchecked since it is eliminated by erasure

```

- ２つ目のcase節に該当せずに、最後（3つ目）のcase節にマッチしてしまった。

```Scala:sbtConsole
scala> var result = class_check(1000000)
var result: String = 不明

```

- 以下は、String型とConditionedInt型のいずれでもない。
- 不明と表示されるのでOK。

```Scala:sbtConsole
scala> var result = class_check(30000)
var result: String = 不明

```

- String型なので最初のcase節に該当している。正しい動き。

```Scala:sbtConsole
scala> var result = class_check("a")
var result: String = 文字列

scala> classCheck("aa")
val res17: String = 文字列

```

- List[Int]型は最後のcase節にマッチすべきで、正しくそのように認識されている。

```Scala:sbtConsole
scala> classCheck(List(1, 2))
val res18: String = 不明

```

- 以下はConditionedInt型と定義されるべき。

```Scala:sbtConsole
scala> classCheck(100000)
val res19: String = 不明

```

- typeで、Int型にInt2型という別名を与える。
- Int2型をcase節の条件に設定すると、ちゃんと動く。

```Scala:sbtConsole
scala> def classCheck(a: Any) = {
     |   a match {
     |     case name: String   => "文字列"
     |     case number: Int2 => "Int2型"
     |     case _              => "不明"
     |   }
     | }
def classCheck(a: Any): String

scala> classCheck(100)
val res20: String = Int2型

scala> 
```


####やり方を変えてみる。

https://www.ne.jp/asahi/hishidama/home/tech/scala/match.html

> 別に定数を定義すれば出来る。
>
>```Scala:
>def know(c: Class[_]) = {
>  val ClassString = classOf[String]
>  val ClassInt    = classOf[Int]
>
>  c match {
>    case ClassString => "文字列"
>    case ClassInt    => "整数"
>    case _           => "知らね"
>  }
>}
>```


```Scala:sbtConsole
scala> def classCheck(c: Class[_]) = {
     |   val ClassString = classOf[String]
     |   val ConditionedInt = classOf[ConditionedInt]
     |  
     |   c match {
     |     case ClassString    => "文字列"
     |     case ConditionedInt => "有料顧客の課金額"
     |     case _              => "不明"
     |   }
     | }
def classCheck(c: Class[_]): String

scala> 
```

- Stringのcase節にマッチしない。何かおかしい。


```Scala:sbtConsole
scala> classCheck("abc")
                  ^
       error: type mismatch;
        found   : String("abc")
        required: Class[_]

scala> 
```


```Scala:sbtConsole
scala> classCheck(200000)
                  ^
       error: type mismatch;
        found   : Int(200000)
        required: Class[_]

scala> 
```

- ConditionedInt型は定義できている。

```Scala:sbtConsole
scala> val tt:ConditionedInt = 200000
val tt: ConditionedInt = 200000

scala> val tt:ConditionedInt = 20
                               ^
       error: Left predicate of (!(20 < 10000) && (20 < 10000000)) failed: Predicate (20 < 10000) did not fail.

scala> 
```
                  

###以下も失敗

```Scala:sbtConsole
scala> val vi = ("John", 123)
val vi: (String, Int) = (John,123)

scala> vi
val res24: (String, Int) = (John,123)

scala> var (a, b) = vi
var a: String = John
var b: Int = 123

scala> a
val res26: String = John

scala> b
val res27: Int = 123

scala> var (a:String, b:Int) = vi
var a: String = John
var b: Int = 123

```

- これは正しい挙動

```Scala:sbtConsole
scala> var (a:Int, b:Int) = vi
              ^
       error: scrutinee is incompatible with pattern type;
        found   : Int
        required: String

```

- bは123で、ConditionedInt型の値制約を充足しないのでエラーが正しい。

```Scala:sbtConsole
scala> var (a:String, b:ConditionedInt) = vi
                        ^
       error: scrutinee is incompatible with pattern type;
        found   : ConditionedInt
           (which expands to)  eu.timepit.refined.api.Refined[Int,eu.timepit.refined.boolean.And[eu.timepit.refined.boolean.Not[eu.timepit.refined.numeric.Less[10000]],eu.timepit.refined.numeric.Less[10000000]]]
        required: Int

```

- 120000は、ConditionedInt型の値制約を充足する。しかし、エラーになる。
- Intが必要、とメッセージが出ている。

```Scala:sbtConsole
scala> var (a:String, b:ConditionedInt) = ("Mike", 120000)
                        ^
       error: scrutinee is incompatible with pattern type;
        found   : ConditionedInt
           (which expands to)  eu.timepit.refined.api.Refined[Int,eu.timepit.refined.boolean.And[eu.timepit.refined.boolean.Not[eu.timepit.refined.numeric.Less[10000]],eu.timepit.refined.numeric.Less[10000000]]]
        required: Int

```

- タプルを変数に入れて（束縛して）からマッチングさせても、ダメ
- Intが必要、とメッセージが出ている。

```Scala:sbtConsole
scala> val sample_tuple = ("Mike", 120000)
val sample_tuple: (String, Int) = (Mike,120000)

scala> var (a:String, b:ConditionedInt) = sample_tuple
                        ^
       error: scrutinee is incompatible with pattern type;
        found   : ConditionedInt
           (which expands to)  eu.timepit.refined.api.Refined[Int,eu.timepit.refined.boolean.And[eu.timepit.refined.boolean.Not[eu.timepit.refined.numeric.Less[10000]],eu.timepit.refined.numeric.Less[10000000]]]
        required: Int

scala> 
```

###List.collectも失敗

```Scala:sbtConsole
scala> val list = List("str", 123, 'symbol, "abc")
warning: 1 deprecation (since 2.13.0); for details, enable `:setting -deprecation` or `:replay -deprecation`
val list: List[Any] = List(str, 123, Symbol(symbol), abc)

scala> list
val res3: List[Any] = List(str, 123, Symbol(symbol), abc)

```

- String型は実行成功する。

```Scala:sbtConsole
scala> list.collect{ case s:String => s }
val res4: List[String] = List(str, abc)

scala> var result = list.collect{ case s:String => s }
var result: List[String] = List(str, abc)

scala> result
val res5: List[String] = List(str, abc)

scala> println(result)
List(str, abc)

scala> 
```

- ConditionedInt型は認識されない。

```Scala:sbtConsole
scala> var result = list.collect{ case s:ConditionedInt => s }
                                         ^
       warning: non-variable type argument Int in type pattern eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Interval.ClosedOpen[10000,10000000]] (the underlying of ConditionedInt) is unchecked since it is eliminated by erasure
var result: List[ConditionedInt] = List()

```

- ２つの要素200000, 3200000が、ConditionedInt型のオブジェクトとして認識されない。

```Scala:sbtConsole
scala> val list2 = List("str", 123, 200000, 3200000, 'symbol, "abc")
warning: 1 deprecation (since 2.13.0); for details, enable `:setting -deprecation` or `:replay -deprecation`
val list2: List[Any] = List(str, 123, 200000, 3200000, Symbol(symbol), abc)

scala> var result2 = list2.collect{ case s:ConditionedInt => s }
                                           ^
       warning: non-variable type argument Int in type pattern eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Interval.ClosedOpen[10000,10000000]] (the underlying of ConditionedInt) is unchecked since it is eliminated by erasure
var result2: List[ConditionedInt] = List()

scala> println(result2)
List()

scala> 
```


###参考

https://www.ne.jp/asahi/hishidama/home/tech/scala/match.html

>##型のマッチング
> マッチさせたい型の値と比較するだけでなく、型そのものを比較対象とすることが出来る。

https://docs.scala-lang.org/ja/tour/pattern-matching.html

>##型のみでのマッチング
> 以下のように型のみでマッチすることができます。