A programming language idea: no static code, explicit caller context (Kite)

H. S. Teoh hsteoh at qfbox.info
Wed Apr 29 22:51:59 UTC 2026


On Wed, Apr 29, 2026 at 07:34:04PM +0000, Marconi via Digitalmars-d wrote:
[...]
> Based on your feedback, I think the core idea is better expressed as:
[...]
> caller represents authority/policy, not just context

I like this idea, at least conceptually.  Not sure about the
implementation, but this makes sense: callee often does not have
sufficient context to know what to do with some resources that it
requires, e.g. memory allocation.  A linked-list container
implementation for example needs to allocate / free memory, but should
it use malloc/free, ARC, GC, per-frame allocation, or some other scheme?
It can't know that.  And linked-list algorithms are technically
independent of allocation scheme.  So it should be written agnostically
of allocation scheme, and let the upper-level code handle the decision.

OTOH, this can't be taken to extremes.  If an algorithm needs, say, a
quick scratch buffer for a couple of variables, like 32 or 64 bytes, it
would be much faster to use a static buffer instead of paying for the
overhead of calling (via indirection, no less!) an unknown
caller-decided allocator.  Static binding eliminates a ton of
boilerplate that's not even needed in this case, and cuts out L1 cache
misses so it can get the job done pronto, instead of dancing the
indirection / abstraction dance and taking more time to allocate
resources than it actually spends on making progress with computing its
result.  At a certain point, indirection / abstraction stops making
sense, and the programmer should have the ability and freedom to bypass
what isn't actually needed.


> libraries can request resources but should not silently decide
> policies

How would you implement a library that provides allocation policies?


> external effects are accessed through explicit capabilities, not
> globals

In principle I agree that impure code should be minimized...  But there
comes a point when you just have to face the fact that the world is
impure.  You can abstract the OS all you want, but at some point, a
global OS setting is a global OS setting; changing it will have
far-reaching consequences across unrelated code whether they expect /
like it or not.  You can dress an impure OS call in whatever pure
clothes you want, but at the end of the day, a global setting in the OS
is still a global setting, it isn't gonna go away just because you
decided to purge your language of impure semantics.  If anything, this
impedance mismatch will come back to bite you later, when you discover
that your language cannot express an impure effect the OS actually
implements, so your code is unable to work with it (because it's not
expressible).


> So perhaps the real principle is:
> 
> Pure code is free. Effectful code requires authority.
[...]

In spite of any flaws, it's interesting to see where you take this idea.
I think there's something to it.

In past projects I've written things like this before:

```d
	struct GuiConnection {
		void onEvent(Event ev, void delegate(GuiApi api) dg) { ... }
	}

	struct MyObject {
		this(GuiConnection conn) {
			sched.onEvent(Event.startup, (api) {
				api.openWindow(...);
				api.createWidget(...);
			});
			sched.onEvent(Event.input, (api) {
				api.updateWidget(...);
			});
			sched.onEvent(Event.close, (api) {
				api.deleteWidget(...);
				api.closeWindow(...);
			});
		}
	}
```

Basically, GuiApi serves as a callback object for MyObject to access
GuiConnection methods without requiring a circular dependency on
GuiConnection, and without actually exposing said methods to code that
shouldn't have access to it (the GuiApi object is only passed to code
that actually needs it; you cannot get an instance or use its methods
outside of that).

This is basically D-speak for what you call "caller authority".  Calling
the `api.XXX` methods is essentially calling back the caller for various
functions ("authority") the caller provides.  Of course, in D you have
to spell it out, so it's a bit more verbose than one would like.  It
would be nice to have some syntactic sugar to factor away the many
references to `api.  ...`, but the essence of it is there.

The syntactic sugar becomes more important when you have multiple APIs
that you want to expose to the callee.  At some point it becomes too
much boilerplate for every callee to take a GuiApi object, an Allocator
object, a Filesystem object, a NetworkSocket object, a SystemTimer
object, and a BusinessLogicDatabase object.  Nobody is going to write
code this way if you have to spell out these APIs every time you call a
function.  So language support becomes a big factor to enable this
idiom.

//

As for why I'd even want to write code like this to begin with, there
are several motivations:

- Minimalistic code: code that doesn't do more than what it needs to,
  and doesn't use more resources than it needs to, to do its job.

- Avoiding Schlemiel code: where you write a linked-list implementation
  for ints, and then another linked-list implementation for floats, and
  then another linked-list implementation for structs, all different
  from each other.  Or where you write your app once for Windows, and
  then rewrite it for Linux, and then rewrite it again for Android, and
  then rewrite it yet again for Web, etc..

  I don't want to have to rewrite stuff.  I want the logic not to depend
  on the specifics of the environment / platform / OS it's running on,
  so that I can easily port it to whatever else there is out there.  I
  want to rewrite only the platform-dependent parts of the app each time
  I port it, not rewrite it from scratch every time.  Only the
  platform-dependent parts of the app should need rewriting each time I
  port it; I shouldn't have to comb through the entire codebase and
  rewrite it in 100 different places.

  This applies not just to porting or algorithms, but to anything
  environmental.  Like malloc/free vs. ARC vs. GC, or local filesystem
  vs. network filesystem, or screen output vs. internal buffer for
  unittesting, etc..  The code shouldn't care what allocation scheme is
  in use, or what it's outputting to. As long as there's an API for it
  to get the memory it wants, or for it to write output to, that's good
  enough, it can do its job, it doesn't have to (and shouldn't) know the
  rest.

- Reusability: if I write an algorithm for defeating the best AI chess
  player, it shouldn't be tied to the windowing system it was written
  on; I should be able to call the same function whether it's to play
  chess with the user sitting at the keyboard, or to interact with a
  network player, or to discover an unusual move combo in an internal
  game state that's never displayed (e.g., as a recursive part of itself
  exploring the state space to find the optimal move).  Or it is being
  called from a unittest to verify that core logic hasn't broken with
  the latest feature addition.

TL;DR: I don't want to have to rewrite code, ever.  I want to write it
once, debug it once, and use it forever -- for anything, including stuff
I haven't thought of yet. On systems that haven't even been invented
yet.


T

-- 
An army of toddlers came marching out of the daycare as their caretakers led them to their waiting parents.  They're the infantry.


More information about the Digitalmars-d mailing list