To introduce more type safety, we can either use tagged type provided by shapeless
or create a class which extends AnyVal
. What are the differences and advantage/disadvantage to use one over the other?
Example:
trait CountryCodeTag
type CountryCode = String @@ CountryCodeTag
class CountryCode(code: String) extends AnyVal
type CountryCode = String @@ CountryCodeTag
+ String @@ CountryCodeTag
is a subtype of String
, i.e. all methods from String
can be used directly: countryCode.toUpperCase
.
− String @@ CountryCodeTag
can be accidentally used where some String
is expected, i.e. it's less type-safe.
− Creating new values is a little awkward: "a".asInstanceOf[String @@ CountryCodeTag]
or val tagger = new Tagger[CountryCodeTag]; tagger("a")
.
− Dependence on Shapeless (although this can be done manually).
class CountryCode(code: String) extends AnyVal
+ It's more type-safe.
− Methods from String
are available with some extra efforts:
class CountryCode(val code: String) extends AnyVal
new CountryCode(countryCode.code.toUpperCase)
or
class CountryCode(val code: String) extends AnyVal
object CountryCode {
def unapply(...) = ...
}
countryCode match { case CountryCode(code) => new CountryCode(code.toUpperCase) }
or
case class CountryCode(code: String) extends AnyVal
countryCode.copy(code = countryCode.code.toUpperCase)
+ Creating new values is a little more natural: new CountryCode("a")
.
+ No extra dependencies (it's plain Scala).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With