wffl

vaguely burnt

  • it/its

I do stuff; pfp by spicymochi



artemis
@artemis

I've been writing some Haskell again the past couple days, and something that has struck me is how much I miss move-semantics. Like, I'm not complaining about it being garbage collected, that's great and makes a lot of things easier.

No what I'm talking about is being able to easily express concepts like "Whatever value you pass into this function, you can't use it anymore after you pass it in! But you can have this other return value derived from it"

Like for example say I had a queue of work waiting to be executed. I want to write a function that at a high level expresses "if there's a job that can be run right now, remove it from the queue and give it to me, otherwise leave the queue unchanged and give me Nothing".

In rust- well in idiomatic rust you'd probably take a mut reference to the queue, take the element out, return that element or None- But imagine we were using rust with immutable datastructures like some kinda weirdo. I could write a function like this:

fn popNextRunnable(queue: WorkQueue) -> (Option<Job>, WorkQueue) {
  // do something here
}

Notice I didn't take queue by reference, I took it by value. That means the rust compiler knows that the queue is getting moved into the function and can never be used again! It disappears off into the void forever! But you get a new WorkQueue back to replace it, don't worry, and maybe you get a Job to run too.

Well it turns out this is called Affine Types, which is related to Linear Types (there's a cool chart on wikipedia/Structural type system). And I'm hooked.

There's not really a good way to express this in haskell to my knowledge, you kinda just have to document "hey please don't pass the original workqueue around anymore afterwards ok? it won't be meaningful to anyone". And you could corral yourself into the right behavior with the State transformer, but like, still nothing stops you from holding onto the old state. Maybe someone has hacked something together though, never bet against a haskell type-system programmer.

It looks like Haskell 9.0.1 landed an experimental extension for linear types, and that's pretty cool. I think I could write some of the things I want with this, once it's not experimental.

But the thing that's funny to me about all this is I didn't even know I was learning how to use linear/affine types. I just, learned them, as a consequence of learning how to use Rust. But now I get it. Now I get why they're useful and DANGIT now I want them available everywhere!


You must log in to comment.

in reply to @artemis's post:

shit! i thought "affine type" describes a type that cannot perform arithmetic directly, but can be differenced. for example, timestamps cannot be added together meaningfully, nor divided or multplied, but they can be differenced (producing a duration typed value, which type does have arithmetic operators with itself and scalars) and they can also be averaged. timestamps have this property, pointers have this property, so do a few other things. non-relative spatial coordinates, i think.

what the heck is this actually called? i can't find it anywhere now because google is dead and useless in the year 2023 of the common era

friend of mine suggests you could be thinking of Affine Spaces https://en.wikipedia.org/wiki/Affine_space

In an affine space, there is no distinguished point that serves as an origin. Hence, no vector has a fixed origin and no vector can be uniquely associated to a point. In an affine space, there are instead displacement vectors, also called translation vectors or simply translations, between two points of the space.[1] Thus it makes sense to subtract two points of the space, giving a translation vector, but it does not make sense to add two points of the space. Likewise, it makes sense to add a displacement vector to a point of an affine space, resulting in a new point translated from the starting point by that vector.

"But imagine we were using rust with immutable datastructures like some kinda weirdo". I was gonna say that this feels familiar from the builder pattern, which I've seen used in rust pretty often. But thinking about it those tend to take mut self rather than actually being immutable?