The announcement from README says:
It features a newly developed scheme for memory management, which was needed
because Javascript has no finalizers. Memory management in Sodium is 100%
automatic.
This scheme imposes one small requirement on the API: You must declare any Sodium
objects held inside closures using wrapper functions lambda1, lambda2 ..
lambda6 - depending on the number of arguments that the closure has.
These take a second argument, which is a list of the Sodium objects contained
in the context of the closure. For example: [...]
I'm sceptical about the "one small requirement" part. Counter-test:
test('should test hold + mapC', (done) => {
const s = new StreamSink<number>();
const c = s.hold(0);
const out: number[] = [];
// const kill1 = c.listen(() => {}); // fix
s.send(1);
const c2 = c.map((x) => x * 3);
const kill2 = c2.listen(a => {
out.push(a);
if (out.length === 1) {
done();
}
});
// kill1(); // fix
kill2();
expect(out).toEqual([3]); // actual: [0]
});
This test fails. I commented two lines that make the test pass. I understand why, I know Sodium implementation details well enough. But there was no closure that I could attach c
to so it's kept alive.
Is the only way to have this system working is to manually manage a "fake" listener on c
? Or is this situation considered "wrong" use of Sodium? In my (limited) experience, cases like this happen in practice and I don't consider them non-pure FRP, or anything like that.