3
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 5 years have passed since last update.

refinedを導入してみた件

Posted at

refinedとは

より細かいな型の指定を行うためのscalaのライブラリです。
例えば、scalaは、他言語における UIntUnsigned Int のような正の値に限定した型指定ができませんが、refinedを用いるとそういったことができるようになります。

Github

導入

build.sbtのライブラリ依存性に以下のように追加します。

libraryDependencies ++= Seq(
  "eu.timepit" %% "refined" % "0.9.0",
)

変数定義を行う場合

以下のように通常の変数定義と同じような記述で扱うことができます。

import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.numeric._

val positive: Int Refined Positive = 5
val negative: Int Refined Positive = -5 // コンパイルエラー
/*
Error:(7, 40) type mismatch;
 found   : Int(-5)
 required: eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Positive]
    (which expands to)  eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Greater[shapeless._0]]
lazy val num: Int Refined Positive = -5
                                      ^
*/

上記の場合は、typeでエイリアスを定義しているので、以下のように省略が可能です。

import eu.timepit.refined.auto._
import eu.timepit.refined.types.numeric._

val positive: PosInt = 5
val negative: PosInt = -5 // コンパイルエラー

ただ、変数定義の時にrefinedを用いるよりも、関数定義の時に用いる方がより便利だと思います。

関数定義を行う場合

例えば、メソッドの引数として渡される値を0より大きい値にしたい場合を想定します。

○ refinedを使わない場合

正と負の整数や0が引数として渡ってくるため、その値によってメソッドの内の処理を書き分けなければなりません。

def validate(num: Int): Int = if (num > 0) num else 1

validate(3)   // Int = 3
validate(-3)  // Int = 1

○ refinedを使う場合

負の整数や0が引数として渡ってきた場合、コンパイル時にエラーとして弾いてくれるため、メソッド内の処理を書き分ける必要がなくなります。

import eu.timepit.refined.auto._
import eu.timepit.refined.types.numeric._

def validate(num: PosInt): Int = num

validate(3)   // Int = 3
validate(0)   // コンパイルエラー
validate(-3)  // コンパイルエラー

/* エラーメッセージ
Error:(16, 12) type mismatch;
 found   : Int(-3)
 required: eu.timepit.refined.types.numeric.PosInt
    (which expands to)  eu.timepit.refined.api.Refined[Int,eu.timepit.refined.numeric.Greater[shapeless._0]]
validate(-3)
          ^
*/

このように、引数として渡ってくる値を制限できるため、関数内のロジックが複雑になることを抑制できます。

その他の型指定の例

こちらに列挙されているのを使ってみました。
https://github.com/fthomas/refined#provided-predicates


import eu.timepit.refined._
import eu.timepit.refined.api.Refined
import eu.timepit.refined.auto._
import eu.timepit.refined.types.numeric._
import eu.timepit.refined.numeric._
import eu.timepit.refined.boolean._
import eu.timepit.refined.string._
import eu.timepit.refined.collection._
  • 偶数の整数型
val even: Int Refined Even = 10
// コンパイルエラー
//val even: Int Refined Even = 9
  • 奇数の整数型
val odd: Int Refined Odd = 9
// コンパイルエラー
//val odd: Int Refined Odd = 10
  • nの倍数の整数型
val divisible3: Int Refined Divisible[W.`3`.T] = 9
// コンパイルエラー
//val divisible3: Int Refined Divisible[W.`3`.T] = 10

  • 0以上の整数型
val zero: Int Refined NonNegative = 0
val overZero: Int Refined NonNegative = 10
// コンパイルエラー
//val nonNegative: Int Refined NonNegative = -10
  • 特定の文字から始まる文字列型
val startWithS: String Refined StartsWith[W.`"S"`.T] = "Start"
// コンパイルエラー
//val startWithS: String Refined StartsWith[W.`"S"`.T] = "Goal"
  • 特定の文字で終わる文字列型
val endWithS: String Refined EndsWith[W.`"l"`.T] = "Goal"
// コンパイルエラー
//val endWithS: String Refined EndsWith[W.`"S"`.T] = "Start"

備考

  • EndsWith[W. ? .T] のように [W. ? .T] がついているところは、直感的に EndsWith["S"] のように書きたいところですが、それだと動かないので注意が必要です。
3
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
3
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?