The buzzword-laden phrase is “global referentially transparent namespace”, but what does this mean?
global: the context you’re reading it from is irrelevant. reading key K on a programmable loom in the 19th century and reading key K on a computer constructed entirely from mental meditation on the capital planet of the galactic empire in the year 999999 work the same.
referentially transparent: if a read has a result, it always has that result, galactic empires nonwithstanding.
namespace: names, which we sometimes call “keys”, indicate “values”, whatever those are. the obvious thing everyone goes for is a hierarchical key system, with keys like /anoma/538209580/325029850/32985032850/[more opaque numbers elided]/3258093285032/storage/2024-06-08T10:05:27,802856745+00:00/ray/references-for-examples/counters/counter-1. But there could be non-obvious things which are better; many things don’t form natural hierarchies! Often what we wish we had is “tags” on values.
The “scry” semantic works as follows:
scry(type, key) reads the value at fully qualified global &c. key key with expected type type. There are three possible results for this operation:
negative result. this key has no value and never will.
positive result. here’s the value, which will be the same for all time and all space.
the above two could be collapsed into just “a result is read”. they’re separate because type and type + 1 are different types.
not returning at all. nontermination could happen in a few ways; one useful way is to block until getting one of the two real result types, another is to produce a crash, but the semantic remains nontermination. it could terminate later, as in the blocking case, but sometimes in practice you affirmatively return nontermination, just like sometimes a program loops forever and sometimes it merely crashes to a higher virtualization level.
keys may go unavailable after having had a result read from them at a prior time; this is fine. you can always throw the computer into the ocean, as long as you never change the value read when a value is actually read. so, for example, if your lawyer tells you to expunge this key (say its value is a copy of disney’s latest movie), all you can do is cause the system to nonterminate on attempts to read it. similarly if your read is somehow unauthorized under system semantics, the result is nonterminate (unless it’s some kind of system where the credential is in the key itself, but in that case the credential must either never or always have access to the value - at least at the level of scry; the higher level system servicing scry attempts can always make that credential’s reads nonterminate.)
sometimes this nontermination is in fact just blocking until the value is available; maybe the computer wasn’t thrown into the ocean but just unplugged from the network for a bit. this sort of thing depends on implementation decisions in both the reader and the servicer of read attempts.
Without loss of generality, I think we can craft a virtual namespace where we understand keys as predicates over values, where a particular value can be checked against a key simply by checking the predicate. Lookups then return sets of values, and we relax slightly referential transparency to simply guarantee that if a particular set s is returned at local time t, the set returned at t + n for any non-negative n will include all elements of s.
Examples of keys-as-predicates:
the simple content-addressed predicate \v -> hash v == h for some fixed h
an example unspent resource lookup \v -> let r = asResource v in unspentInController c t r for a fixed controller c and controller-time t, where unspentInController checks the controller signature, proper format, etc. attesting to the created-but-not-consumed resources at t
a more complex unspent resource lookup which also checks that the resource label is equal to some constant
a lookup of some particular piece of oracle data, e.g. \v -> isOracleFormat v t && checkSignature o v for some fixed oracle o and time t
A few questions arise here:
How do we actually encode functions? This one isn’t too difficult; see here. We can also hash the encoded functions for constant-sized keys.
How do we actually fetch the right data? Obviously not all functions will be easily computable, and in the limit case, we would have to iterate over all data and test the predicate in order to return, which is infeasible. In practice, only a subset of known predicates will be supported - but the relaxed referential transparency guarantee also always allows us to return {} before appropriate indexes for a particular predicate are created. In some cases (e.g. resources at some time), it would be helpful to have affirmative proof that a response is complete.
grumble, grumble - discussed this with @isheff recently: there is a ‘right way’ to do it but it would need a few researcher-years to realise. i think with generic encoding for code it is not a forever burden to just do something suboptimal for now, though
i will note this way of using predicate functions is basically the same as the way i proposed topics should work (i think this was when we met in march) cc @tg-x@nzarin
This reminds me of some kind of graphical namespace system.
I can imagine using this in the following ways:
We can reflect these keys and pathways in a programming languages module/object system. Instead of having a traditional tree structure, we can form the connections as graphs, and chose how resolution Works
For Example
We can refer to functions by hash, this is a flat reference and likely one we’d persist to disc after we walk the graph.
We could use the global semantics laid out by @ray for pathing down a referenced name in a nested way
We could say resolve some object by looking at the graph on how we got to a point
We could do a programming rules based on the graph relations
The main issue is that if we want any rules, like inheritance rules, I’m not sure how this multi graph approach for resolution would work. Would reserving a path namespace somehow imbue properties onto the given object? How would we manage this, can an object viewed in a different light be given new features (a new kind of mixin!?!??!)
We want to pun as much as possible, to keep the system simpler, the fewer rules, the more emergent behavior we’ll have and help shape and evolve the system.
We don’t need to fix a subset of predicates. We will build some generally supported one as we go yes. However design care should be had to make this reflective, to allow the user to extend it themselves by adding new predicates.
You should make this a general tool if you want to design it all, otherwise you’ll want to make new tools by the limits of this, and have designs only you have imagined, which is not what you want.
As a side note I’m not sure your predicates are referentially transparent, even in your extension. The set of unspent resources changes as time goes on. Rooms available change, and will not give consistent results. They do give the same result at a same timeshot, however your predicate are only on data and not time, meaning that the properties you wanted are not had (I.E. I spend x at time t + 1, so now the unspent predicate now lacks x) .
Further I’d be interested if you would view OP as a predicate, or do you not consider global namespacing a want?
My example was for the set of unspent resources controlled by a particular controler at a particular point in that controller’s logical time (a block height), which does not change.
What is a “Room” ?
I’m not sure that I follow this question. Content-addressed functional predicates are a global namespace. Do you mean something else, perhaps related to human-readable keys?
I do think it should be made more explicit that most useful namespaces will contain some kind of time information.
You can imagine modeling a game where one may make types/classes. And in the process of data modeling we may refine the type/ inherit from the class. However, as our design goes on, we may toss some types/classes
Thus design one could be
.
├── Potions
└── Weapons
└── Sword
└── Fire
However as time goes on, we may learn the folly of our ways and design the namespace hierarchy like this:
Since these are namespaces, it would pointless for us to query for all the types/classes under Sword, as we have scrapped designs like Sword/Fire!
Instead we’d probably want to query something like “What is the hierarchy of the latest tagged version of the game system”. Or what is the “latest hierarchy of the game system from all the developers machine”.
I think we could for certain subsets of query express in the given type/class meta behavior based on the namespaces they happen to inhabit.