Home > other >  How to normalise a Union Type (T | Option[T])?
How to normalise a Union Type (T | Option[T])?

Time:11-12

I have the following case class:

case class Example[T](
    obj: Option[T] | T = None,
)

This allows me to construct it like Example(myObject) instead of Example(Some(myObject)).

To work with obj I need to normalise it to Option[T]:

  lazy val maybeIn = obj match
    case o: Option[T] => o
    case o: T => Some(o)

the type test for Option[T] cannot be checked at runtime

I tried with TypeTest but I got also warnings - or the solutions I found look really complicated - see https://stackoverflow.com/a/69608091/2750966

Is there a better way to achieve this pattern in Scala 3?

CodePudding user response:

I don't know about Scala3. But you could simply do this:

case class Example[T](v: Option[T] = None)

object Example {
 def apply[T](t: T): Example[T] = Example(Some(t))
}

CodePudding user response:

We have a specialized Option-like type for this purpose: OptArg (in Scala 2 but should be easily portable to 3)

import com.avsystem.commons._

def gimmeLotsOfParams(
  intParam: OptArg[Int] = OptArg.Empty,
  strParam: OptArg[String] = OptArg.Empty
): Unit = ???

gimmeLotsOfParams(42)
gimmeLotsOfParams(strParam = "foo")

It relies on an implicit conversion so you have to be a little careful with it, i.e. don't use it as a drop-in replacement for Option.

The implementation of OptArg is simple enough that if you don't want external dependencies then you can probably just copy it into your project or some kind of "commons" library.

CodePudding user response:

I think that the already given answer is probably better suited for the use case you proposed (exposing an API can can take a simple value and normalize it to an Option).

However, the question in the title is still interesting and I think it makes sense to address it.

What you are observing is a consequence of type parameters being erased at runtime, i.e. they only exist during compilation, while matching happens at runtime, once those have been erased.

However, the Scala compiler is able to perform flow analysis for union types. Intuitively I'd say there's probably a way to make it work in pattern matching (as you did), but you can make it work for sure using an if and isInstanceOf (not as clean, I agree):

case class Example[T](
    obj: Option[T] | T = None
) {
  lazy val maybeIn =
    if (obj.isInstanceOf[Option[_]]) {
      obj
    } else {
      Some(obj)
    }
}

You can play around with this code here on Scastie.

Here is the announcement from 2019 when flow analysis was added to the compiler.

  • Related