( 後記 )
以下の記事に、「ユーザ定義の依存型のクラス名(type別名)を使って、型レベルのパターンマッチ」を行う方法を解節しています。
このコードが実行エラーになる。
Refinedで定義した値制約に、__typeキーワード__で__別名ConditionedInt__を付けた後、
sbtConsolescala> type ConditionedInt = Int Refined Interval.ClosedOpen[10000, 10000000] type ConditionedInt scala>
case節のマッチング対象に、Stringと並んで、__ConditionedInt__を指定すると、認識されない。
sbtConsolescala> def class_check(a: Any) = { | a match { (省略) | case _: ConditionedInt => "有料顧客の課金額"
( エラー表示 )
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__を読み込みます。
scalaVersion := "2.13.6"
libraryDependencies += "eu.timepit" %% "refined" % "0.9.27"
- sbt consoleコマンドでREPL環境を立ち上げる。
% 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)の要素数2のTupleを扱います。
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> 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
- 2つ目のcase節に該当せずに、最後(3つ目)のcase節にマッチしてしまった。
scala> var result = class_check(1000000)
var result: String = 不明
- 以下は、String型とConditionedInt型のいずれでもない。
- 不明と表示されるのでOK。
scala> var result = class_check(30000)
var result: String = 不明
- String型なので最初のcase節に該当している。正しい動き。
scala> var result = class_check("a")
var result: String = 文字列
scala> classCheck("aa")
val res17: String = 文字列
- List[Int]型は最後のcase節にマッチすべきで、正しくそのように認識されている。
scala> classCheck(List(1, 2))
val res18: String = 不明
- 以下はConditionedInt型と定義されるべき。
scala> classCheck(100000)
val res19: String = 不明
- typeで、Int型にInt2型という別名を与える。
- Int2型をcase節の条件に設定すると、ちゃんと動く。
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>
やり方を変えてみる。
別に定数を定義すれば出来る。
def know(c: Class[_]) = { val ClassString = classOf[String] val ClassInt = classOf[Int] c match { case ClassString => "文字列" case ClassInt => "整数" case _ => "知らね" } }
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> classCheck("abc")
^
error: type mismatch;
found : String("abc")
required: Class[_]
scala>
scala> classCheck(200000)
^
error: type mismatch;
found : Int(200000)
required: Class[_]
scala>
- ConditionedInt型は定義できている。
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> 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> var (a:Int, b:Int) = vi
^
error: scrutinee is incompatible with pattern type;
found : Int
required: String
- bは123で、ConditionedInt型の値制約を充足しないのでエラーが正しい。
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> 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> 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> 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> 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> 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()
- 2つの要素200000, 3200000が、ConditionedInt型のオブジェクトとして認識されない。
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>
参考
##型のマッチング
マッチさせたい型の値と比較するだけでなく、型そのものを比較対象とすることが出来る。
##型のみでのマッチング
以下のように型のみでマッチすることができます。