šŸ’¾ Archived View for dcreager.net ā€ŗ swanson ā€ŗ primitive-primitives.gmi captured on 2024-02-05 at 10:04:04. Gemini links have been rewritten to link to archived content

View Raw

More Information

ā¬…ļø Previous capture (2023-09-28)

-=-=-=-=-=-=-

Primitives should be primitive

2022-07-12

So far I've been trying to provide primitives as a regular Swanson unit, which you load just like any other. (Currently doing that via a ā€œloaderā€, which is the only value passed in to a unit's entry point. Previously via a dependency injection kind of thing, where the entry point's inputs were the names of its dependencies, and the host took care of loading those before passing control to the entry point.)

Recently I was trying to figure out what a formal definition of the execution model would look like. And in that mathy world, I don't think it's appropriate to piggyback on module loading like that. Yes you _can_, but that doesn't mean that you _should_.

Instead, I'm thinking that it's more reasonable to have primitives actually be primitive. In a formalism, I'd represent this with another bag-of-crap in the context of the typing and operational semantics judgments. (Ī , maybe?)

I can see two possibilities in the implementation. One is that entry points would take in a parameter called ā€˜primitivesā€™. This would be an invokable, very much like the ā€˜kernelā€™ unit that I'm currently providing. Again, we _could_ make this work (we already are!), but I'm not sure that it's the best approach. It seems like it might be more cumbersome to implement, since you'd have to ensure linearity. (Each method would have to return the primitives invokable as a ā€˜$_ā€™ result, you'd have to provide ā€˜dropā€™ and ā€˜cloneā€™ methods, etc.)

The other possibility would be to have primitives be completely implicit, and add another terminal statement to the execution model. We already have ā€œinvokeā€, which invokes a value in the environment. We'd add ā€œinvoke primitiveā€, which would invoke an implicit primitive method. (With ā€œinvokeā€, the invocation target is removed from the environment before passing control. With ā€œinvoke primitiveā€, it wouldn't, since there isn't any value in the environment representing the primitives.) This approach is most aligned with how I anticipate the formalism version of this would work. And it seems like it would be easier to implement.

On the other hand, using a different terminal statement means you couldn't override the behavior of the primitives. It might be useful to invoke an entry point while passing in a _different_ value to masquerade as the primitives. Maybe for test cases, maybe for having more control over the unit loader process, who knows.

There's a possible way to close the circle here, by (of course) adding a layer of indirection. Primitives would be latent, but units would still takes in a parameter. (Possibly called ā€˜kernelā€™ to distinguish it from the latent primitives?) The kernel would use the special ā€œinvoke primitiveā€ statement liberally. Other units typically wouldn't use the primitives directly, and would use the kernel instead. To override the behavior of the kernel, we'd make sure that you could locate a unit without loading it (i.e., getting its entry point as a value without invoking it), so that you could then invoke it with some other value that can masquerade as a kernel.

..