So I see that in dotty parameters can be marked inline
. What does this actually mean? What does it change about how an linline def expands? Here’s a toy example to motivate my question.
package tokendb
trait Buffer[B, T] { def length(b: B): Int }
object Buffer {
implied LongArrayBuffer1 for Buffer[Array[Long], Long] {
def length(b: Array[Long]) = b.length
}
}
So the idea is that I can write various ops against a buffer abstraction, and use inlining to get efficient implementation code. Let’s do a silly transform - double the length.
val ls = Array.ofDim[Long](42)
def twiceLength[B, T](b: B) given (B: Buffer[B, T]): Int = B.length(b) * 2
val a = twiceLength(ls)
// val a: scala.Int = tokendb.TestBuff.twiceLength[scala.Array[scala.Long], scala.Long](ls)(tokendb.Buffer.LongArrayBuffer1)
Not surprisingly it’s compiled down to the obvious implementation
inline def twiceLengthInlined[B, T](b: B) given (B: Buffer[B, T]): Int = B.length(b) * 2
val b = twiceLengthInlined(ls)
// val b: scala.Int = (tokendb.Buffer.LongArrayBuffer1.length(ls).*(2): scala.Int)
This is better. The call is inlined. However, it’s still going through the length abstraction on Buffer. Granted it’s resolved it to the concrete implementation.
inline def twiceInliningParam[B, T](b: B) given (inline B: Buffer[B, T]): Int = B.length(b) * 2
Unfortunately this fails to compile with:
no implicit argument of type tokendb.Buffer[Array[Long], T] @InlineParam was found for parameter B of method twiceInliningParam in object TestBuff
I was hoping that it would recursively unpack the instance of Buffer.length
to get us back to ll.length
.
So I guess I have 2 questions. Firstly, what are inline parameters and how and why do you use them? Secondly, can we get things to recursively inline across typeclass instances?