Only one option can be non-null in case class

Viewed 57

I have a case class with two options

case class MyClass(
  first: Option[String] = None,
  second: Option[Boolean] = None
)

Is there a way in Scala to specify at the type level that both first and second can't both be set? So if MyClass.second.isDefined then MyCLass.first must be None and vice versa, or both can be None.

This is possible in TypeScript.

1 Answers

So, how about this (the following code is for scala3, but it is easy to implement in scala2):

scala> @annotation.implicitNotFound("Need (None, Some) or (Some, None), found (${F}, ${S})")
     | trait Cons[F, S]
     |
     | object Cons:
     |   given Cons[Some[String], None.type]  = new Cons {}
     |   given Cons[None.type, Some[Boolean]] = new Cons {}
     |
     | case class MyClass[F <: Option[String], S <: Option[Boolean]](first: F, second: S)(using c: Cons[F, S])

scala> val m1 = MyClass(None, Some(true))
val m1: MyClass[None.type, Some[Boolean]] = MyClass(None,Some(true))

scala> val m2 = MyClass(Some(""), None)
val m2: MyClass[Some[String], None.type] = MyClass(Some(),None)

scala> val m3 = MyClass(None, None)
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 |val m3 = MyClass(None, None)
  |                            ^
  |                            Need (None, Some) or (Some, None), found (None.type, None.type)
1 error found

scala> val m4 = MyClass(Some(""), Some(false))
-- Error: --------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1 |val m4 = MyClass(Some(""), Some(false))
  |                                       ^
  |                                       Need (None, Some) or (Some, None), found (Some[String], Some[Boolean])
1 error found
Related