š¾ 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
ā¬ ļø Previous capture (2023-09-28)
-=-=-=-=-=-=-
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.