8

There were already quite a few discussions on Stackoverflow about proper ways of using Optional in Java (Discussions like this one, or this)

As of now, using Optional for class members in Java is widely recognized as a code smell and even discouraged by fact that it deliberately does not implement Serializable interface. Also, we should avoid it in DTOs, constructors and methods' input parameters. From OOP point of view everything I have read so far about Optional appeals to my reason.

My question is, does FP side of Scala change something in a way we should use Optional ? Especially since implementation of Optional in Scala seems to be way richer. I have found plenty of articles describing how to use it in Scala, but not a single one that exhaust topic when should I use it and when should I not.

Community
  • 1
  • 1
Mateusz Chrzaszcz
  • 1,240
  • 14
  • 32
  • 1
    Even though many well established libraries (e.g. ScalaTest) use `Option` fields, my personal take is that they are a code smell. In many cases, you can eliminate `Option` fields by defining a small hierarchy of more focused types instead of just a single type. – jub0bs Feb 04 '17 at 16:18
  • 1
    @Jubobs Exactly what you wrote. As for now Optional fields are at least controversial for me, as every time I see it I feel it's overused and makes developers too lazy to think about cohesion. – Mateusz Chrzaszcz Feb 04 '17 at 16:20
  • 1
    I'm tempted to post an answer with an example, but I'm afraid your question might get closed; it might have a better home at http://softwareengineering.stackexchange.com. – jub0bs Feb 04 '17 at 16:22
  • @Jubobs I really do think such example might be useful in this topic, so go for it. In worst case scenario someone will moderate this ;) – Mateusz Chrzaszcz Feb 04 '17 at 16:28
  • @Jubobs when referring other sites, it is often helpful to point that [cross-posting is frowned upon](http://meta.stackexchange.com/tags/cross-posting/info) – gnat Feb 04 '17 at 21:06

2 Answers2

11

Short answer

Option fields have use cases; they're not intrinsically bad. However, even though several well established libraries (e.g. ScalaTest) define classes with Option fields, the latter, IMO, tend to be a code smell, as they often try to do too much for their own good.

In many cases, a type containing optional fields can easily and advantageously be replaced by an algebraic data type.

An example

The domain

Consider a business domain dealing with accounts. An account starts its life one day as an open account, but may eventually be closed. Accounts, among other data, contains the dates on which they were open and closed, where applicable.

Using an Option field

Here is an implementation of an account, using an Option field:

final case class Account(openedOn: LocalDate, closedOn: Option[LocalDate], ...)

We also have an account service, which defines, among other things, a close method:

trait AccountService {
  // ...
  def close(account: Account): Account
}

This approach is problematic, for a number of reasons. One problem is that Account isn't particularly performant: because closedOn is a "boxed" type, you have one level of indirection too many, so to speak. Moreover, Account's memory footprint is less than ideal: a "closed account" contains a pretty uninteresting value (None), which is a waste of space.

Another, more serious, problem is that the close method cannot enforce, at the type level, that the parameter be an "open account" and the result be a "closed account". You would have to write tests to check that this business rule is enforced by your implementation.

Using a small ADT (and eschewing Option fields)

Consider the following alternative design:

sealed trait Account { ... }

final case class OpenAccount(openedOn: LocalDate, ...) extends Account

final case class ClosedAccount(openedOn: LocalDate, closedOn: LocalDate, ...) extends Account

This small ADT remedies the performance problem, but there is more... You can now encode the business rule at the type level! This is an example of making illegal states unrepresentable (a phrase attributed to Yaron Minsky). As a result, your service's API becomes more expressive and harder to misuse:

trait AccountService {
  // ...
  def close(account: OpenAccount): ClosedAccount
}

This example may be sufficient to convince you that the second approach is preferable, and that Option fields are best avoided (or, at least, used sparingly).

Resources

For more more about eliminating optional fields towards making illegal states unrepresentable, see

jub0bs
  • 60,866
  • 25
  • 183
  • 186
  • this is a good example.. but isn't None a single object in scala.. so I am not sure to what extent the memory argument is valid. – rogue-one Feb 04 '17 at 19:37
  • 1
    @rogue-one I should clarify: my issue isn't with `None` itself; it's that `Account` holds a reference to `None`, which seems a bit of a waste. `OpenAccount` holds no such reference. – jub0bs Feb 04 '17 at 19:44
  • 1
    cool.. as with any feature overuse misuse of feature is not a good thing.. but there are plenty of scenarios were a Option would make sense. for eg if an Account object had an optional attribute called phone no.. then Option[PhoneNo] would make sense and if there are many such business specific optional fields then Option would make sense since creating separate types for each case would lead to class explosion.. – rogue-one Feb 04 '17 at 19:48
  • @Jubobs though I guess we all agree that overhead of such reference is not really an issue – Mateusz Chrzaszcz Feb 04 '17 at 19:48
  • 1
    @MateuszChrzaszcz Yes, I was specifically referring to the memory issue in my last comment, which isn't that big a deal, but the model involving `None` remains less than ideal. – jub0bs Feb 04 '17 at 19:49
  • @MateuszChrzaszcz You have to think about cache misses and the likes too, though. It's not just the memory footprint, it's the performance penalty of accessing the data (two levels of indirection instead of one in the case of `Some(date)`). – jub0bs Feb 04 '17 at 19:52
8

Option of scala implements Serializable

Usage of Option in scala is highly recommended for nullable attributes. Option[T] is considered better than T because the former is more typesafe than than latter.

As of now, using Optional for class members in Java is widely recognized as a code smell

on the contrary presence of null in-place of Optional attribute in scala is considered a code-smell.

As much as Scala is a functional language it also a language that promotes type-safety. In an Ideal world a truly fully typesafe language will not have runtime exceptions like NullpointerException and Option plays a important role in Scala to avoid it.

The Option[T] explicits states that the attribute can be in the state of null (i.e. None) and forces the clients of the attribute to handle the null scenario. Thus Option adds more information to the type system and makes the code more typesafe.

With language feature such as such as pattern matching and Monad/Monoid the economics of using Optional datatypes in Scala is very cheap and user friendly in Scala compared to Java.

Pattern matching:

 optionalVariable match {
   case Some(x) => /* handle when variable has a value*/
   case None => /* handle when the variable doesn't have a value*/
 }

Option as Monad:

optionalVariable foreach { x => /* run code when the variable is defined*/ }
optionalVariable map { x => /* map optional type to another type */}

Edit:

Jubobs makes very good case where using Option can be replaced with custom types. But I think there are many more cases where Optional attributes make more sense. For eg: If the Account object has optional attributes such as emailId and phoneNo then Option[T] would be a better solution since creating custom types for each combination would be impractical and would lead to class explosion.

rogue-one
  • 11,259
  • 7
  • 53
  • 75
  • 1
    What about cohesion? Let's say I have case class A(paramB: Option[B], paramC: Option[C], paramD: Option[D]), isn't it actually promoting lowly cohesive classes? Too often I see stuff like that instead of separate classes... – Mateusz Chrzaszcz Feb 04 '17 at 16:04
  • 1
    @MateuszChrzaszcz IMHO I think Optional datatypes and cohesion of classes are quite orthogonal – rogue-one Feb 04 '17 at 16:15
  • 3
    That's not related. Having a datatype with all its properties optional is questionable at design level, whatever is the way used to encode a single optional property. – cchantep Feb 04 '17 at 16:15
  • 1
    @rogue-one I disagree. A class with one or more `Option` types often tries to do too much. – jub0bs Feb 04 '17 at 16:18
  • @Jubobs yes.... if you shove too much down into single class then having many Optional fields in a class .. is symptom of the problem and not THE problem.. – rogue-one Feb 04 '17 at 16:22