From (get-env "FOO") to (environment.FOO) to (env.FOO) to $FOO

If you spend time in bash scripting or something like GitHub CI, you realize that environment variables are really important.

So it's a bit sad that Rebol has had the really ugly get-env and set-env functions as the interface to this.

I'd say it's pretty clear that we'd like for env.FOO to work. I'm also leaning to thinking that at least in some dialected circumstances, $FOO should be interpreted as an environment variable as well...we have these parts:

  • $WORD
  • $TU.P.LE
  • $PA/TH
  • $[BL O CK]
  • $(GR O UP)

So things like CALL/SHELL should let you use those.

But How To Make An "ANY-CONTEXT!" That Calls Functions?

Right now, the only way to get hooks into the system to run code when you use tuple access is by making a new datatype. And that can only be done in the C code.

So if someone put a gun to my head and told me to make it work right now--today--the quickest path would be to make a new ANY-CONTEXT! type in C called an ENVIRONMENT!. The flaky part is that environment variables come from an extension, and so it would be an "extension type" (like a GOB! or VECTOR!), and those are second-class citizens (they don't work in TYPESET! for instance, they're all considered the same CUSTOM! datatype for those purposes).

I resist the temptation to do this, because while it would be nice to write environment.FOO instead of get-env "FOO", most of the uses are in the rebmake code...which needs to work in the bootstrap executable. So no point in making an existing mess with extension types messier.

Any Usermode Ideas?

I think what we should be looking for is a way that someone without a C compiler could make something that looks like an object, but the behavior for handling the TUPLE! access comes from functions.

Right now you can't "dot an action"... actions only take PATH!s for refinements. So we could say that if we get a dot, it looks to see if the function has a /DOT refinement, and if it does then it will call it accordingly with information from the context of the get or set:

environment: func [variable [word! set-word!] value [<end> any-value!] /dot] [
    assert [dot]  ; let's say you always use with dot for now
    if set-word? variable [
       echo [You asked to set @variable to @value]
    ] else [
       echo [You asked to get @variable]
]

>> environment.FOO
You asked to get FOO

>> environment.FOO: 10
You asked to set FOO to 10

It's better than nothing. But what I don't like about it is that I have the idea that tuple access on functions will be able to get and set properties stored on that function... e.g. that functions will be able to act as objects. This would be where stuff like help informaiton is stored. (This was historically called the "meta object" but we have much more compelling uses for the word "meta" now.)

So Probably Better to be a User-Defined Datatype

Unfortunately these don't exist yet. But I guess now we have a good example of ENVIRONMENT! as something that needs supporting.

2 Likes

So I noticed generalized accessors bring some promising ideas.

But they aren't quite what's needed here. You could make an object with a field in it that was able to read or write an environment variable when accessed via TUPLE! or SET-TUPLE!. But making an object that had no literal fields but did the tuple decoding itself is another thing.

Fortunately tuple processing is now built on PICK and POKE. So what's necessary here is to make a way for objects to have their own custom PICK and POKE methods.

The flakiness is fading away, and there's little to stop me now from making an ENVIRONMENT! datatype.

It would implement PICK and POKE, and as such be able to do selection of environment variables via TUPLE!.

We have standardized methods now where if you say env.FOO and it doesn't find it, then you get a raised error. So you say try env.FOO and get NULL, or maybe env.FOO and get VOID.

It can be supported in typechecking, though questions about how extension types participate in things like ANY-CONTEXT? are still up in the air.

I think you should be able to do things like:

 >> env.FOO: "demo"

 >> group: bind env '(FOO)
 == (FOO)  ; bound

 >> eval group
 == "demo"

It might not be useful to bind a block of code in general to environment variables--due to the possibility of the environment to expand to contain anything and thus compete with your words of code. But having a COMPOSE variant that binds GROUP!s to the environment could be quite useful, as you could say (FOO) or $(FOO) did environment variables.

You could do an EVAL-IN-ENV construct, that composed a block of code where just instances of $FOO-style things were composed... though that would snapshot the environment variable state before the evaluation. You couldn't pick up modifications as they happened. That would require being able to actually hook the evaluator... which... yes is still on the table.

What To Call It?

I guess calling it just system.environment puts it in a good place, and system is shorthanded to sys so it could be similarly shorthanded as sys.env.

Then if you needed to you could say env: system.environment or e: sys.env or whatever shorthand you thought was appropriate.