4 ms·
You'd take out each File from the Bunch and close it, and then deallocate the Bunch. This can be abstracted into a function. >That seems like it'd suck for per
by zetalyrae 4y ago
You'd take out each File from the Bunch and close it, and then deallocate the Bunch. This can be abstracted into a function.
>That seems like it'd suck for performance.
In either C++ or Rust, the exact same thing is happening. Except that you don't see it in the source code, because the compiler injects destructor calls for you at the end of a block.
But there's nothing magical about destructors. The exact same code is being executed as in Austral, only Austral has 1) no surprise control flow and 2) no hidden function calls, so you have to see the code that is going to be executed.
It might look something like this:
-- bunch has type Bunch[File]
while not isEmpty(&bunch) do
let f: File := pop(&!bunch);
closeFile(f);
end while;
disposeBunch(bunch);
- tialaramex 4y agoDid you try this? Because while you're correct in terms of the semantics, the performance (which is why I chose that word) is quite another matter. Specifically it's not at all uncommon for some collection (such as our arbitrary Bunch) to offer a far cheaper way to throw away the whole Bunch than to remove every Thing in the bunch one at a time. With a destruction mechanic this is fine, the collection destroys all the Files at once, but if the user must hand roll a loop like yours - taking each File out of the Bunch and then closeFile(file) then they can't take advantage of the improved performance.
- zetalyrae 4y agoIt's not about removing each element individually vs. deallocating the whole collection. It's that you have to close each file handle individually. In C++ you might iterate over the bunch, close each file, and then deallocate the whole thing. In Austral, because of how linear types work, you have to take a linear value out of the collection, destroy it, then, when the collection is empty, you can safely deallocate it. There doesn't have to be a performance difference between the two because popping an element from a bunch does not imply resizing the bunch or doing any allocations. In either language, the effectful actions are the same: close n file handles, deallocate the memory for a bunch.
- tialaramex 4y ago> It's that you have to close each file handle individually. My emphasis. This is where the problem creeps in. Let's see that again: > In C++ you might iterate over the bunch, close each file, and then deallocate the whole thing. In Rust or C++ actually the Bunch will destroy and thus close all the files. Not me, I just destroyed the whole Bunch, not my problem how that works. > There doesn't have to be a performance difference between the two If your description had been more precise it might be apparent where a performance difference comes from. The Bunch knows the intimate details of its internal workings, so it may be able to do significantly less "housekeeping" while it is cleaning up. Without that knowledge however, when I am cleaning up the Bunch has no idea what my ultimate intent is and so it needs to do proper housekeeping. I think you could try to mitigate that, at a cost of further language complexity, by introducing another Linear type, the ToBeDestroyedBunch which knows it is destined to be destroyed and is just here while the caller destroys the Things which were in the Bunch. So, when I want rid of the whole Bunch I call a function with a Bunch, and I get back a ToBeDestroyedBunch. This needn't do all the housekeeping on each removal because we're obliged to dispose of it properly after we're finished and we can no longer do any other operations on a Bunch because we don't have a Bunch only a ToBeDestroyedBunch.
- zetalyrae 4y agoThe Bunch API could easily include a function that takes a bunch and a function `t -> ()` that deallocates individual members, so that the logic is encapsulated in a function. You have to pass the destructor function explicitly because the language doesn't know which function is the "canonical" destructor (and a type may have multiple destructors, i.e. multiple ways to end its lifecycle).
- tialaramex 4y agoSure, the idea of potentially having different ends to the lifecycle makes sense to me, same way constructors aren't a thing in Rust. And yes, I agree that this solves the problem, which was why I suggested it as one of three ways this could work if you look back up the thread. Not sure about how Austral can assure itself the Linear contractual obligations were discharged here, but I might be overthinking it.