TaggedTypesを使うと、型にメタ情報を与えることができる。
例えば以下の例だと、Person#name
, Company#name
が同じくString型なので、両者を混同してしまう可能性がある。
case class Person(name: String)
case class Company(name: String)
def findCompanyByName(companyName: String): Option[Company] = ???
val person = Person("John")
findCompanyByName(person.name) // 当たり前だが、コンパイルできる
ここで、こういった型エイリアスを定義しておくと、
Tagged.scala
type Tagged[T] = {type Tag = T}
type @@[+T, Tag] = T with Tagged[Tag]
def tagged[T, Tag](a: T): T @@ Tag = a.asInstanceOf[T @@ Tag]
以下のように「Person用のStringである」ことを表せて、しかも間違えた時にコンパイルエラーにできる。
これをTaggedTypesという。
TaggedTypesの実装方法はいくつかあって、上にあげたのはscalaz 7.0.xで使われている方法。
scalaz 7.1以降からは実装方法が変わっている。(https://github.com/scalaz/scalaz/pull/693)
case class Person(name: String @@ Person)
case class Company(name: String @@ Company)
def findCompanyByName(companyName: String @@ Company): Option[Company] = ???
val person = Person(tagged("John"))
val company = Company(tagged("opt"))
findCompanyByName(person.name) // 通らない
findCompanyByName(company.name) // 通る
また、この方法で実装したTaggedTypesでは以下のようなことができる(scalaz 7.1以降ではできない)
- もとの型のメソッドがよべる
val x: String @@ X = tagged("x")
x.split(",") // よべる
- Stringとして扱える
val x: String @@ X = tagged("x")
def foo(a: String): Unit = ???
foo(x) // 通る
参考
がくぞさんのスライドがわかりやすい
http://gakuzzzz.github.io/slides/refactoring_in_scala/#1