Search code examples
scalascala-2.11

Scala Some, None and my custom Default


We have Some, None in Scala and I need the third one named Default. The idea behind is:

I don't want to completely "shadow" Some, None with my custom types. I want let user use them and apply my own "Default" at certain cases.

@Michael Zajac gave several options, I think the frist one is pretty good.

I can have built in Some, None and "artificial" like

case object DefaultParamHere {
    def apply(): Option[String] = {
      Some("Substitute me we default framework value, please")
    }
  }

Thanks!

so use case will be:

case class Application(artifact: Artifact,
                              mainClass: Class[_],
                              jvmMemory: Option[Memory] = None,
                              jvmOpts: Option[String] = DefaultParamHere(),
                              applicationConfiguration: List[String] = Nil) extends Stage

Solution

  • You have a several options, really. You could:

    Use a sentinel value. For example, if you're working with Option[Int], Some(-1) could encode the "default" but not None value. Client code would need to know this value needs to be handled specially, which makes this solution weak. It's easy to implement, but difficult for those that need to use code that returns sentinel values. I wouldn't recommend this approach.

    Use a different type, such as Option[Either[Default, B]]. In this solution, Some(Right(a)) would represent an existing value, Some(Left(Default)) would represent your special case, and None would still represent the lack of data entirely. Default could be as simple as a case object that signals something you need to do, or some sort of data type that contains default values the program may need to deal with. In my opinion, this is the best option (pun intended), as it makes it clear exactly what a method may be returning when returning this type, and it forces code to deal with the Default case.

    Use a different type, such as Option[Option[A]]. Here Some(Some(a)) would represent an existing value, Some(None) could represent your special case, and None the same as before. This is similar to the previous solution, except that there would not be a way to encode additional information within the default case, and it would be a little more cryptic as to what a method returning this type was actually meant for.

    Use a different type, such as scalaz.Either3. Similarly to the previous two solutions, you could encode your three cases using Either3[None, Default, A], where Right3(a), Middle3(Default), and Left3(None) would be the respective cases. This again would make it clear what the type is intended for and force client code to deal with all three cases. However, it is likely overkill for what you need. Still, if you ever wanted to encode more information within the None case, Either3 is always out there.

    Use a wrapper. It's difficult to say exactly what this would look like without knowing more about your use-case, but you could make a simple class that wraps Option and can supply the default you want.

    case class Option3[A](opt: Option[A], default: A) {
      def getOrDefault: A = opt.getOrElse(default)
    }
    

    This of course, is probably not what you want exactly, but something along these lines. You could also provide an implicit conversion from your wrapper to Option that can substitute a default value in None cases, or otherwise, depending on what you really want it to do.