0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

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

Last updated at Posted at 2021-11-08

( 後記 )

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

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

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

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

scala>

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

sbtConsole
scala> 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__を読み込みます。
build.sbt
scalaVersion := "2.13.6"
libraryDependencies += "eu.timepit" %% "refined" % "0.9.27"
  • sbt consoleコマンドでREPL環境を立ち上げる。
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)の要素数2のTupleを扱います。
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型__を組み入れる。
  • エラーが吐かれた。
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

  • 2つ目のcase節に該当せずに、最後(3つ目)のcase節にマッチしてしまった。
sbtConsole
scala> var result = class_check(1000000)
var result: String = 不明

  • 以下は、String型とConditionedInt型のいずれでもない。
  • 不明と表示されるのでOK。
sbtConsole
scala> var result = class_check(30000)
var result: String = 不明

  • String型なので最初のcase節に該当している。正しい動き。
sbtConsole
scala> var result = class_check("a")
var result: String = 文字列

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

  • List[Int]型は最後のcase節にマッチすべきで、正しくそのように認識されている。
sbtConsole
scala> classCheck(List(1, 2))
val res18: String = 不明

  • 以下はConditionedInt型と定義されるべき。
sbtConsole
scala> classCheck(100000)
val res19: String = 不明

  • typeで、Int型にInt2型という別名を与える。
  • Int2型をcase節の条件に設定すると、ちゃんと動く。
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> 

やり方を変えてみる。

別に定数を定義すれば出来る。

def know(c: Class[_]) = {
 val ClassString = classOf[String]
 val ClassInt    = classOf[Int]

 c match {
   case ClassString => "文字列"
   case ClassInt    => "整数"
   case _           => "知らね"
 }
}
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節にマッチしない。何かおかしい。
sbtConsole
scala> classCheck("abc")
                  ^
       error: type mismatch;
        found   : String("abc")
        required: Class[_]

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

scala> 
  • ConditionedInt型は定義できている。
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> 

以下も失敗

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

  • これは正しい挙動
sbtConsole
scala> var (a:Int, b:Int) = vi
              ^
       error: scrutinee is incompatible with pattern type;
        found   : Int
        required: String

  • bは123で、ConditionedInt型の値制約を充足しないのでエラーが正しい。
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が必要、とメッセージが出ている。
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が必要、とメッセージが出ている。
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も失敗

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型は実行成功する。
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型は認識されない。
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()

  • 2つの要素200000, 3200000が、ConditionedInt型のオブジェクトとして認識されない。
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> 

参考

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

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

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?