Naming Option variables
Many times while writing Scala, I find myself having to look up the type of a variable that I’m working with. Often it is because I’m unsure whether the variable is an Option. This means having to depend on my IDE shortcut to show me the type or having to navigate to the source file.
1 2 |
val hotel : Hotel = ??? val stateCode = hotel.stateCode // <b><1></b> |
- Is stateCode an Option?
Other times, I’m transforming a given Option variable into its base type and I’m forced to make up an awkward name for the inner name of the value contained in the Option:
1 2 3 4 5 6 7 8 9 |
def getRawPhoneNumber(...) : Option[String] = ??? def doSomething(s: String) : Future[Unit] = ??? val phoneNumber = svc.getRawPhoneNumber(...) phoneNumber match { case Some(gotAPhoneNumber) => doSomething(gotAPhoneNumber) // <b><1></b> case None => Future.succcessful(()) } |
- Ugly! What to name this value?!?
While this example is trivial, it gets complicated fast once you have 15 or so fields to deal with at once:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
case class Hotel( id: Int, chainCode: Option[String], name: String, addressProvider: AddressProviderEnum, addressLine1: String, addressLine2: Option[String], city: String, stateCode: Option[String], postalCode: Option[String], countryCode: String, phoneNumber: String, location: PlaceGeocode, providerMapping: Set[ProviderHotelId], propertyType: PropertyTypeEnum ) |
- Without having to flip back to this source file, I don’t know which fields are Option!
I’ve started using a super-simple naming convention that explicitly declares the cardinality of the variable to both avoid the naming problem and to increase readability of the code. For Option, I simply add the prefix “opt” to the Option variable:
1 2 3 4 5 6 |
val optPhoneNumber = svc.getRawPhoneNumber(...) optPhoneNumber match { case Some(phoneNumber) => doSomething(phoneNumber) // <b><1></b> case None => Future.succcessful(()) } |
- Ahhhh so much better. I know optPhoneNumber is an Option AND I have an obvious (and highly readable) name for the inner variable.
Naming collection variables
There is also another problem that I encounter when naming things: what the heck do I name collection variables? For plural words in English, the convention is mostly to just append s. And this mostly works:
1 2 3 |
val hotels : Vector[Hotel] = ??? val names : Vector[String] = ??? val phoneNumbers : Vector[String] = ??? |
Except when it doesn’t work:
1 2 |
val amenities : Vector[String] = ??? // <b><1></b> val amenitys : Vector[String] = ??? // <b><2></b> |
- Ack. Ugly!!
- Ugly and people think I don’t know basic English grammar. They won’t understand my need for regularity.
Also, there is a more serious problem here. If a collection has at least one item, it is always safe to call head. This condition can often be guaranteed at compile time, but how would anyone know? This leaves me with the same problem I had with null in Java: I must either defensively test everywhere for the condition OR I must depend on it being in the documentation.
1 2 3 4 5 6 7 |
val hotels : Vector[Hotel] = ??? val unsafeFirst = hotels.head // <b><1></b> val optSafeFirst = hotels.size match { // <b><2></b> case 0 => None case _ => Some(hotels.head) } |
- Maybe this is wrong? Hopefully, documentation guarantees that hotels is non-empty.
- Or I can be safe about this. But now I have an Option and my code grows ever more complex (for possibly no good reason if hotels is guaranteed to have at least one item).
In code I write now, I fix both of these problems by keeping the variable name in the English singular, and prefixing a plural variable prefix to the variable name:
- “zom”: Read as “zero or more”
- “oom”: Read as “one or more”
- “all”: Read as “all of them” (Most likely a very large collection, but technically equivalent to “zom”)
1 2 3 4 5 6 7 8 9 10 11 12 |
val amenities : Vector[String] = ??? // <b><1></b> val zomAmenity : Vector[String] = ??? // <b><2></b> val oomAmenity : Vector[String] = ??? // <b><3></b> val amenity = oomAmenity.head val allAmenity : Vector[String] = ??? // <b><4></b> allAmenity.foreach { amenity => // <b><5></b> ... } |
- Ugly. Also is head safe?
- I know head is not safe here
- I know there is at least one amenity. head is safe.
- I know there should be at least one amenity. head is probably safe. More importantly I should avoid excessive transforms on the collection, all those copies will take up a lot of memory!
- Bonus! Like Option above, I have an easy and readable decision for what to name the inner function parameter.
Cardinality Prefix naming convention
Here is the full listing of the cardinality prefix naming convention:
- Name all variables in the English singular form
- Ex: hotel, phoneNumber, amenity
- If the variable is an option, prefix with “opt”
- Ex: optHotel, optPhoneNumber, optAmenity
- If the variable is a collection AND guarantees at least one member, prefix with “oom”
- Ex: oomHotel, oomPhoneNumber, oomAmenity
- If the variable is a collection AND does not guarantee at least one member, prefix with “zom”
- Ex: zomHotel, zomPhoneNumber, zomAmenity
- If the variable is a collection of all of the values (very large collection), prefix with “all”
- Ex: allHotel, allPhoneNumber, allAmenity
Example
The large case class from above, after applying the naming convention:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
case class Hotel( id: Int, optChainCode: Option[String], name: String, addressProvider: AddressProviderEnum, addressLine1: String, optAddressLine2: Option[String], city: String, optStateCode: Option[String], // <b><1></b> optPostalCode: Option[String], countryCode: String, phoneNumber: String, location: PlaceGeocode, oomProviderMapping: Set[ProviderHotelId], // <b><2></b> propertyType: PropertyTypeEnum ) |
- I don’t need to read the source to know optStateCode is an Option
- I know oomProviderMapping has at least one item and that head is safe to call