Is there a reason why <#2978> made NullableRaise, ...
# arrow
y
Is there a reason why #2978 made NullableRaise, OptionRaise, etc not value classes anymore? I know it has barely any impact because of escape analysis and that whenever they're used as the
Raise
interface they're boxed, but it's not really explained anywhere in the PR as to why that happened.
s
If I recall correctly it is because of the
@JvmName
y
Oh so it's to facilitate Java using Raise methods?
s
No, not really. It’s a JVM signature conflict that needs to made be non-ambiguous using
@JvmName
y
Oh I see now yeah
@JvmName
errors out inside value classes. That's truly annoying. It feels like JvmName should have better compatibility with the language features (like interfaces for instance)
If Java compatability isn't a priority, then why are the raise functions inside of a
JvmMultifileClass
? Is it a nice-to-have? For better logs perhaps?
s
Uhm, it’s a pattern I’ve taken over from KotlinX Coroutines. They couple all functionality (of
Flow
) into a single class. It’s a nice to have really. It results in slightly nicer logging, slightly smaller binary footprint afaik. Nothing major.
Yes, it would be nice to leverage
@JvmName
from interfaces or value classes but I always thought this was due to technical limitations
y
Gotcha, that makes sense. I just got bitten by JvmMultilineClass with a signature conflict (Making a PR for a
merge(Raise.() -> Blah
that's analogous to
Effect.merge
and it conflicted) so I was curious why it was there.
I think for interfaces it's perhaps related to binary compatibility or something? Although it is fine for abstract classes though I'm not sure. It might just be a limitation in the compiler where it can't figure out the parent's usage of
JvmName
s
Right, it conflicts because
Effect
is the same than a
Raise
lambda, and the receiver becomes the first argument 😅
y
Yep, seems like the Arrow way for this is to just do
@JvmName("_merge")
. At least that's how
fold
does it
s
Oh, it works for
abstract class
🤔 I don’t use that often, het should remember that.
Yes, I’m not a big fan of
_
but these inline methods are not useable from Java anyway
y
And actually for interfaces you can just suppress the error, but you have to remember to mark all overrides of the method with the same
JvmName
s
Or well what is it again.. recently I had to add some workarounds to use even
non-inline
methods because the signatures were ambiguous on JVM but it compiled fine. It was in a mixed with Scala codebase though 😅
y
Aren't they theoretically useable, but just not very nice? I guess also since there's a signature conflict, it means that Java users can call
fold
since
_fold
does the exact same thing with the same signature
It feels like the distinction of
Effect
and
EagerEffect
code could go away if kotlin just supported inlining receiver lambdas, but alas.
Turns out there's a KEEP to introduce a more-general version of
JvmName
that works with interfaces: https://github.com/Kotlin/KEEP/blob/binary-signature/proposals/multiplatform/binary-signature.md
s
Yes, I wish the difference between
EagerEffect
and
Effect
could go away and it’s kind-of possible with context receivers if we had them 😅 But I’m also curious how that’ll actually be used in practice compared to just returning
Either
. The nice thing is that both can live alongside each other perfectly. Still inlining lambda receivers is something I’ve though about, but I cannot really put into words how it’d want it to actually work in the language 😅 😅 😅
y
inlining receivers just doesn't work well with how type-inference works since the
.
is viewed as depending on its receiver, and not influencing its receiver, and so you can only really have functions on lambdas if those lambdas are pre-existing values, but then those values can't really be inlined. That's why extension lambda receivers for inline functions are treated as
noinline
With the risk of derailing the discussion too much: if the compiler had some sort of way to declare values that shouldn't escape an inline context (similar to how an inline method with an inline lambda parameter can't have that lambda escape) then you can have "local lambdas" that act as values, and hence they'd only be allowed to be passed to inline functions. This allows for viewing data as "curried functions", ala church encoding, and you can have zero-cost struct-like types. I made that as a suggestion a while back, but after having a go at implementing it, it definitely is too big of a hassle, and results in frequent compiler crashes because it just wasn't designed to inline such deeply-nested code. One "easy"-ish way for the compiler to support this would be with a new modifier similar to
crossinline
(maybe
inline
) that declares that this lambda parameter is an
inline
function w.r.t its lambda parameters. An example helps:
Copy code
inline fun <T, R1, R2> letLambda(lambda: (T) -> R1, inline block: ((T) -> R1) -> R2) = block(lambda)
here
inline
declares that the block is, for all intents and purposes, an inline function, and so at the callsite you'd be defining an anonymous inline function. You'd also only be able to use callable references to other
inline
functions. This introduces a name for the lambda parameter inside without actually storing the lambda as a real object. Then, an extension on such a lambda would make sense, and by extension, it would justify having
EagerEffect.fold
being inline and hence applying to
Effect
as well (in suspending contexts only obviously).