refinedでPosInt + PosInt を PosIntにしたい

結論

オーバーフローしたら条件満たさないから無理

結論以外

refinedScalaでrefinement typeを表現するためのライブラリです。

このライブラリの中に「1以上のInt」を表すPosIntという型があるのですが、refinedは PosInt + PosInt = PosIntといった推論を(通常は)行わないので、2つのPosIntを足す場合は自分で検査を行う必要があります。

val a = PosInt.unsafeFrom(1)
val b = PosInt.unsafeFrom(2)
val c: Either[String, PosInt] = PosInt.from(a.value + b.value) 

一方でrefinedでは catsのSemigroupインスタンス が提供されているので、 以下のようにPosIntが返ってくるPosInt同士の計算を行うことができます。

import eu.timepit.refined.cats._
import cats.syntax.semigroup._

val c: PosInt =  a.value |+| b.value

標準で上記のようなメソッドが用意されていないのはオーバーフローによりPosInt同士の加算が負になる = PosIntでなくなる可能性があるためです。 (see https://github.com/fthomas/refined/issues/217#issuecomment-258535569 )

実際、refinedで提供されているSemigroupのインスタンスでは オーバーフローしないような実装 が含まれています。

これが求めるものかどうかはユースケース次第かとは思います。筆者がよく使うシチュエーションではPosInt.fromを使って万が一オーバフローが発生したら例外が出てくれる方が嬉しいので上記インスタンスを積極的に利用することはしていません。

ということで用法用量を守って正しくお使いください。

なおこの記事は来週のアドベントカレンダー用の記事の端材を使って執筆されました。