refinedを導入してみた件

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

# Github
https://github.com/fthomas/refined

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

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

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

```scala
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でエイリアスを定義しているので、以下のように省略が可能です。

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

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

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

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

## ○ refinedを使わない場合

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

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

validate(3)   // Int = 3
validate(-3)  // Int = 1
```
## ○ refinedを使う場合
負の整数や０が引数として渡ってきた場合、コンパイル時にエラーとして弾いてくれるため、メソッド内の処理を書き分ける必要がなくなります。

```scala
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


```scala

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"]` のように書きたいところですが、それだと動かないので注意が必要です。