In the Programming in Scala discussion on when to use case classes and when extractors, they say
Extractors break [the] link between data representations and patterns ... This property is called representation independence ...
If your component had defined and exported a set of case classes, you'd be stuck with them because client code could already contain pattern matches against these classes ...
Fortunately, you need not decide right away. You could always start with case classes and then, if the need arises, change to extractors. Because patterns over extractors and patterns over case classes look exactly the same in Scala, pattern matches in your clients will continue to work.
I'm trying to figure out what this looks like. Suppose, to use their example, that I define emails using
case class Email(user: String, domain: String)
and client Bob and I both and match against it like
def send(addr: Email) = addr match {
case Email(user, domain) => ...
}
send(Email("joel", "gmail.com"))
Then suppose I decide to switch to extractors to gain representation independence as
// case class Email(user: String, domain: String)
object Email {
def apply(user: String, domain: String) = user + "@" + domain
def unapply(addr: String) = addr split "@" match {
case Array(user: String, domain: String) => Some(user, domain)
case _ => None
}
}
this all works except for a compilation failure in send: "not found: type Email". I can fix this with
type Email = String
but is this the right way? And I don't quite see how this has helped me gain representation independence. Sure, Bob can now match against "joel@gmail.com" or Email("joel", "gmail.com") (which are now equivalent), but I have to assume they're using Emails.