I've been sitting on an encoding for lifetimes KEE...
# language-evolution
y
I've been sitting on an encoding for lifetimes KEEP into current Kotlin for the past 2 months; finally had some time to polish it up and write a ReadMe, so here it goes! I believe this is a faithful encoding of the lifetime safety that it offers into current Kotlin. I had to cheat a bit (using a compiler plugin) to not have the syntax be too awful. This actually helped me find a couple of "bugs" (outdated signatures) in the stacks KEEP, so it's been of some practical use! It really helped me understand how the stack primitives library is meant to work and what guarantees it offers, in a way that re-reading the KEEP couldn't quite do. I've written up a nice ReadMe, but the TL;DR is that I'm using
@RestrictsSuspension
to track the local lifetime, and some type parameters to actually give a name to such lifetimes and be able to put constraints on them.
👀 1
c
> The prior art here is some historical version of Arrow that I can't find right now, but trust me, you don't want to find it either. 🤣 0.11.0 is the version where
.fix
was removed, if I remember correctly
HighKT is a great name, because that's probably what it takes to understand the implications
❤️ 2
y
It's a play on highJ as well, but I love that it spells out HKT
c
Honestly I'm a bit scared by lifetimes because they're a concept Kotlin/JVM developers don't have in the languages they've learned. I know about them from Rust, but that's a very different ecosystem. I think a major part of developing Lifetimes will be figuring out the mental model to make them simple to understand, and the communication/syntax to make it readable. The
local
modifier on lambdas is a very good first step, I think that's perfectly obvious. Not sure how to scale that to full blown sub-lifetime things.
y
I'm hoping this gives curious Kotliners with a bit too much free time a chance to look at the mechanisms behind lifetimes. Demystifying it, if you will Good syntax would go a very very long way here; it's vital to have this feature work (I've seen other research languages with lifetime-like things and oh boy, you need a PhD to understand the syntax alone). I do think that advanced lifetime usages are gonna be very niche. You need to have support for them for e.g. the stacks library (literally the peak of advanced lifetimes), but most people will just use
local
modifier, and a bit of
_{foo}
on return types, and that's that. I'm not in love with all the syntax in the KEEP, but it's just design notes, so it's perfectly acceptable. At the very least, it's much better than the monstrosity I have to do in Kotlin right now to express the same concepts
c
I'm not in love with all the syntax in the KEEP, but it's just design notes, so it's perfectly acceptable.
100%. The first step of the design notes is figuring out whether it works, can be made safe, and how it interacts semantically with the rest of the language. Syntax is the 2nd step, but it's still a massive step.
I believe for most language features syntax just 'appears' as "there's obviously one way to represent this", but in the case of lifetimes I think no mainstream language represents them in an obvious way.
y
Well, it's been Rust and Scala so far AFAIK for mainstream languages, and they're both have a research edge to them (Scala's designer, Martin Odersky, is involved with the Effekt team as well). Kotlin tends to be more "industrial" but not shying away from research when it makes sense (e.g. mixed-site generics). I'm pretty confident then that if lifetimes make it in, they'll have better syntax than what's out there. At the same time, I don't mind if the very advanced cases are still hard to understand. The stacks primitives have difficult signatures to understand not because of syntax, but because these primitives are doing a lot and have to deal with many different lifetimes at the same time. No one outside the stdlib and some niche users (hi!) will be writing signatures of such complexity
c
Sure, but there's a chance I end up one of these users lol
❤️ 1
Honestly I don't really understand the implications yet
and I don't have the research background/experience
y
Of lifetime safety? It's a guarantee that things won't escape past where you declared them to exist, so it prevents a bunch of use-after-free bugs. The system has to accommodate a lot of different usage patterns, though, hence the need for it to be in the type system instead of just being a syntactic check. Oh, and it fixes the problems with flows and such by making your coroutine context static, I.e. you can determine where your coroutine context is coming from really easily, instead of being forced to use the one that your caller is using. This lets multiple suspend-based DSLs compose well, which is precisely the problem we have with flows now. A
suspend
-based DSL will now suspend itself precisely to where its "handler" is (I.e. the DSL block, like
Flow
), so you don't get confusion between that and e.g.
launch
Finally, it also lets you pass around non-local control (I.e.basic
return
and also
suspend
, as previously mentioned), so you get
Raise
for free! So basically, it eliminated use-after-free bugs, and makes your code more "compositional" and modular
c
Yeah, meaning it will become the primitive for many high-level libraries and such
Hence it needs to be very powerful (and I trust the team and you to go in that direction) but also easy to understand and build correctly on top of
h
Is it comparable to Swift escaping annotation? If so, why do you need multi shot continuations?
y
Sorry, the description is for a wider project. This branch has nothing multishot about it. The code is based on multishot things I was doing before lifetimes were even announced, and it was a useful base to modify into lifetimes and stacks primitives. I'll change the description for now