Modern Ren-C lets you define locals in function specs using FENCE!.
>> y: 10
>> foo: proc [x {y}] [
y: 20
print ["x plus y is" x + y]
]
>> foo 1000
x plus y is 1020
>> y
== 10
Obviously, LOCAL Is Now Available As A Refinement
It's nice to be able to have functions like get-time:local.
And it's also nice that malicious actors can't slip values into your locals at the callsite.
(Yes, people did actually worry about this in historical Redbol and do paranoid checks...!)
You Can Default Locals (and it's efficient)
The FENCE! uses the "CONSTRUCT dialect"... so you can do things like default your locals:
>> foo: proc [x {y (20)}] [
print ["x plus y is" x + y]
]
>> foo 1000
x plus y is 1020
Defaulting locals actually has a performance and storage advantage, because the slots in the frame are a sunk cost. And you avoid running the assignment code on each call.
But because of that: take note that the computation of the local happens exactly once at creation time and is stored in the archetypal frame:
>> z: 300
>> foo: proc [x {y: z + 4}] [
print ["x plus y is" x + y]
]
>> foo 1000
x plus y is 1304
>> z: -300
>> foo 1000
x plus y is 1304
So you can't use it if what you want the local to be is different on each instantiation.
Locals Default To Null
Because it follows the CONSTRUCT dialect, your locals will default to null. But there are other shorthands:
-
If you want your local to be trash, make it a quasiform:
proc [x {~y~}] [...] -
If you want it to be a void, make it a metaform:
proc [x {^y}] [...]
You Can Define Locals Anywhere In The Spec
And you can have any number of definitions:
foo: func [a {b ~c~} d :e {f} {^g}] [...]
This is of great benefit to generative code.
(It's also used to build "frame-compatible" natives. Some natives are similar but are missing arguments others expect...and if you throw in locals at positions where an argument would be you can use the same hardcoded position numbers for either frame.)
Why It's Like This: Just Look at R3-Alpha FUNCT (!)
Look how complex the locals-gathering FUNCT from R3-Alpha is, as it tries to add some local variables to a spec...
It had to check to see if there was already a /LOCAL, and add it if not:
; Copy the spec and add /local to the end if not found
unless find spec: copy/deep spec /local [append spec [
/local ; In a block so the generated source gets the newlines
]]
Bear in mind that specifying /LOCAL twice would be a duplicate refinement error, and /LOCAL did not have to be at the end of a spec. Things were tricky, because there were "private refinements". These refinements were an artifact of how help worked--they were not shown--but weren't actually private.
So you had to be careful, to insert things after local but not after the private refinements, or they'd be arguments to those refinements:
; Collect all set-words in the body as words to be used as locals, and add
; them to the spec. Don't include the words already in the spec or object.
insert find/tail spec /local collect-words/deep/set/ignore body either with [
...
]