chriswarbo-net: fecd606e17878229bbcae9f7c7424125318a7d49
1: ---
2: title: Optional Datatype Fields
3: ---
4:
5: I've been working on a bunch of Scala codebases recently, which represent data
6: using classes with optional fields. For example (not actual code!):
7:
8: ```scala
9: case class User(
10: userID: String,
11: name: Option[String],
12: email: Option[String],
13: phone: Option[String],
14: dateOfBirth: Option[Date],
15: )
16: ```
17:
18: There are a few problems with this sort of representation:
19:
20: - It's 'stringly typed': using uninformative types like 'String', rather than
21: something more specific like 'UserID', 'Email', etc.
22: - Using this sort of representation is awkward, e.g.
23: `val myUser = User(myID, None, None, None, Some(myDOB))`
24: - This representation does not scale well, as we add new fields.
25:
26: ## Rich Types ##
27:
28: The first problem is easy eough to fix. Values with some particular structure
29: can use a representation which follows that structure. For example, not every
30: `String` is a valid email address: there must be two parts separated by '@', and
31: each part has further restrictions (e.g. the first part cannot be empty, cannot
32: contain the '@' character, etc.; whilst the second part must be a valid domain
33: name, like 'localhost', 'gmail.com', etc.). These constraints give rise to
34: 'correct by construction' representations like the following:
35:
36: ```scala
37: final case class Email(
38: private user: Username,
39: private host: DomainName,
40: ) {
41: override lazy val toString: String = user.toString + '@' + host.toString
42: }
43: ```
44:
45: Types which don't have any known structure can use a 'newtype' (borrowing
46: terminology from Haskell). In Scala we do that with `AnyVal`, like this:
47:
48: ```scala
49: final case class Name(override val toString: String) extends AnyVal
50: ```
51:
52: ## Smarter Constructors ##
Generated by git2html.