Search code examples
scalashapeless

Tagged Type vs class extends AnyVal


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

Solution

  • 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).