In my ETL parsing code, I map optional attributes of optional classes (a lot). This often gives me many lines of code where I’m simply mapping or flat-mapping attributes. It would reduce code verbosity if there was syntactic sugar for getting attributes as Options of their element’s type.
// map example
val n: Option[Name] = Some(Name(first:String = "F", last:String = "L"))
val first: Option[String] = n ? first // expands to n.map(_.first)
// flatMap example
val n: Option[Name] = Some(Name(first:Option[String] = "F", last:Option[String] = "L"))
val first: Option[String] = n ?? first // expands to n.flatMap(_.first)
Could this be accomplished through a macro or compiler plugin?
Interesting idea, but not sure if it is viable with ? symbol. I am pretty sure it is used as a custom operator in some libraries, so it probably would have to be removed from allowed symbols for custom operators in the language.
// row provides an optional string defined by the schema
// HumanName transforms a free text name into a structured name
val name = row(Schema.userName).flatMap(HumanName.apply)
User(
userId = Some(id),
firstName = name.flatMap(_.first),
middleName = name.flatMap(_.middle),
lastName = name.flatMap(_.last),
)
is a small improvement for such a large (breaking) language change. There might be some tool in Shapeless that might help you if you’re really desperate, but I don’t know what. Maybe Records.
You could implement this through a macro (although I think it has to be whitebox in this case) if you changed it a little:
val first: Option[String] = n ?? 'first // expanding to n.flatMap(_.first)
This could be achieved by defining ?? as a method in an implicit class that takes the name of the property as a Symbol (as you can’t pass first on it’s own, due to it not resolving to a valid name on its own).
I could maybe take a go at this, but if you have knowledge of macros this should actually be quite simple.
If I may add in on this as a potential language change though, I’d much prefer ?., mimicking Kotlins safe call operator or C#'s null-conditional operator (other languages implement this too). Also, ?: as an alternative to getOrElse would also be useful.
The problem that I see with this, is that we don’t have T?, we have Option[T]. So there is less syntactic connection than there is in Kotlin, Typescript or C# between nullable types and null-safe access.
I implemented the Kotlin / Groovy flavored ? operator for Scala in Dsl.scala yesterday. Unlike the Option monad solution, the ? operator works with null instead of Option. I recommend you use ? with Java libraries, and Option + !-notation with Scala libraries.
You can use ? annotation to represent a nullable value.
import com.thoughtworks.dsl.keywords.NullSafe._
case class Tree(left: Tree @ ? = null, right: Tree @ ? = null, value: String @ ? = null)
val root: Tree @ ? = Tree(
left = Tree(
left = Tree(value = "left-left"),
right = Tree(value = "left-right")
),
right = Tree(value = "right")
)
Normal . is not null safe, when selecting nullable field of left , right or value .
a[NullPointerException] should be thrownBy {
root.right.left.right.value
}
The above code throws an exception because root.right.left is null . The exception can be avoided by using ? on a nullable value:
root.?.right.?.left.?.value should be(null)
The entire expression is null if one of ? is performed on a null value.
The boundary of a null safe operator ? is the nearest enclosing expression whose type is annotated as ?.
("Hello " + ("world " + root.?.right.?.left.?.value)) should be("Hello world null")
("Hello " + (("world " + root.?.right.?.left.?.value.?): @ ?)) should be("Hello null")
(("Hello " + ("world " + root.?.right.?.left.?.value.?)): @ ?) should be(null)