I’m reasonably in favor of unless from the standpoint of avoiding logical errors in writing and manipulating code. To me, representing business logic is core to what product engineers do.
But maybe the bigger question is whether Scala lacks a reasonable way to implement a zero-cost unless construct in code. I’m guessing the missing features would be second-class functions (for the statement blocks) and less constrained syntax definitions.
Obviously there are some risks of everyone’s Scala looking different, depending on what constructs they might want to adopt, as can be the case in Ruby. But I can also see upsides in less hackery to make DSLs.
Not sure what you mean by second-class functions, but I think an unless implemented in Scala can look reasonably first-class – indistinguishable from native if if you normally use braces with if anyway:
object unlessSyntax {
@inline def unless[A](b: Boolean)(t: => A): Unless[false, A] = {
if (b) Unless(t)
else null.asInstanceOf[Unless[false, A]]
}
private[unlessSyntax] type Unless[Inhabited <: Boolean, +A] <: AnyRef
@inline private[this] def Unless[Inhabited <: Boolean, A](a: A): Unless[Inhabited, A] = a.asInstanceOf[Unless[Inhabited, A]]
implicit final class UnlessSyntax[Inhabited <: Boolean, +A](private val self: Unless[Inhabited, A]) extends AnyVal {
@inline def `else`[B >: A](f: => B): Unless[true, B] = {
if (self eq null) Unless(f) else self.asInstanceOf[Unless[true, A]]
}
}
@inline implicit def unpack[A](unless: Unless[true, A]): A = unless.asInstanceOf[A]
@inline implicit def unpackUnit(unless: Unless[false, _]): Unit = ()
}
import unlessSyntax.unless
//val x: Int = // as expected, one-sided unless cannot be assigned to Int
val x: Unit = unless(1 == 3) {
println("no!")
1
}
// this is fine
val i = unless(true) {
8
} `else` {
4
}
// no boxing:
println(i)
// 8
println {
unless(i / 2 == 1) {
s"xa $i"
} `else` s"xb $i"
}
// xb 8
This is completely free allocation-wise. Should also be free method call-wise if Scalac inliner is enabled or if unless is expanded by a macro.
My previous comment about the word unless got it wrong: it should be pronounced like shapeless. What we’re doing without are negatory prefixes like un or !.
unless should do for boolean syntax what shapeless does for arity.
I believe to match Rust if let (which is really nice, although impure by design) it should return Unit, and not Option (as condOpt) or Boolean (as cond).
Something like:
def ifLet[T](value: T)(handler: PartialFunction[T, Unit]): Unit = {
handler.applyOrElse[T, Unit](value, _ => ())
}
val x: Option[Int] = Some(5)
ifLet(x) {
case Some(value) => println(value)
}
(though this is getting off topic both in language and in construct)