Some people might need answers during their AoC window.
I noticed this works:
object X:
inline def unapply(inline i: Int) = X(i.toLong)
class X(x: Long) extends AnyVal:
def get = x
def isEmpty = x < 0
class C:
def f(i: Int) =
i match
case X(j) => j
case _ => -1L
and similarly in Scala 2 with -opt:inline:'<sources>'
object X {
def unapply(i: Int): X = new X(i.toLong)
}
class X(val x: Long) extends AnyVal {
def get = x
def isEmpty = x < 0
}
class C {
def f(i: Int) =
i match {
case X(j) => j
case _ => -1L
}
def g(ns: List[Int]) =
ns.collect {
case X(j) if j < 10 => j
}
}
yields similar
public long f(int);
descriptor: (I)J
flags: (0x0001) ACC_PUBLIC
Code:
stack=3, locals=4, args_size=2
0: getstatic #17 // Field X$.MODULE$:LX$;
3: pop
4: iload_1
5: i2l
6: lstore_2
7: getstatic #17 // Field X$.MODULE$:LX$;
10: lload_2
11: invokevirtual #21 // Method X$.isEmpty$extension:(J)Z
14: ifne 23
17: getstatic #17 // Field X$.MODULE$:LX$;
20: pop
21: lload_2
22: lreturn
23: ldc2_w #22 // long -1l
26: lreturn
but the partial function for collect is less savory. Well, I guess applyOrElse takes an int but returns a boxed Long.
I see. It’s not obvious to me what a solution would look like, though.
I will say that “an extractor introduces allocation” is probably not going to change minds here. I believe we consider it normal in Scala that a lot of common idioms allocate a short-lived object; doing so is incredibly cheap on the JVM. “Incredibly cheap” is not zero of course, but at that level of micro-optimization, I think a tradeoff between expressiveness and nanoseconds is often inevitable.
Personally, the .map(_.toLong) seems fine to me in isolation; or if you’re doing it over and over again, and extractor seems like an appropriate solution.
The fastparse macro will fusing all these into a single code block as lihaoyi once said in his blog.
but what if this is not a fastparse macro and then the inline will not happing.
I would like to see these is scala is targeting wasm and llvm nowadays , a common optimizer will help all these backend, this is yes a macro optimization, but that will help high performance code where every bits maters:)
object RefExtractor extends App {
class Ref[T](val ref: AtomicReference[T]) extends AnyVal {
def get(): T = {
println("call ref.get")
ref.get()
}
def isEmpty: Boolean = {
println("call ref.get")
ref.get() == null
}
def set(value: T): Unit = ref.set(value)
}
object Ref {
def unapply[T](ref: AtomicReference[T]): Ref[T] = new Ref(ref)
}
val ref = new AtomicReference("hello")
ref match {
case Ref(value) => println(value)
}
}
But what if the x is not directly available?
then you will see:
call ref.get
call ref.get
hello
It would be nice to save one of the call ref.get, @odersky is that possible? I knew this is possible at bytecode level, not sure how to express it at language.