HIstorical Rebol had a quirky trick for implementing static variables, using self-modifying code.
rebol2>> accumulate: func [value /local store] [
store: [0]
store/1: store/1 + value
return store/1
]
rebol2>> accumulate 10
== 10
rebol2>> accumulate 20
== 30
The bizarre thing that's happening here is that each time the function runs, the store variable receives the same BLOCK! identity...a single instance living in the function body.
That gets modified, so if you look at the source, you see it has been updated:
rebol2>> source accumulate
accumulate: func [value /local store][
store: [30]
store/1: store/1 + value
return store/1
]
Ren-C has safety mechanisms with CONST to stop you from writing self-modifying code on accident:
https://rebol.metaeducation.com/t/value-vs-series-modification-bit-const-and-mutable/976
But you can still do this, you just have to say MUTABLE on the block.
Ren-C Statics Dial This Up To 11
>> accumulate: func [value] {
store: static [print "Initializing!" 0]
store: store + value
return store
}
>> accumulate 10
Initializing!
== 10
>> accumulate 20
== 30
Not only can you run arbitrary initialization, but you aren't limited to a static variable that's a BLOCK!... your static can be any kind of value you want!
![]()
So How Does It Work?
It's based on the same principle of mutating the code. But what it does is it mutates the block to hold a variable for static storage. Then, the STATIC function sets up an aliasing relationship between the variable you're assigning the static to and the storage. On subsequent calls it sets up that same relationship.
static: lambda [
"Create static variable with lasting identity across multiple invocations"
[]: [dual?]
@init [block! fence!]
][
if init <> '[static-storage] { ; first run if not equal
static-storage: eval init ; v-- mutate to [static-storage]
insert clear mutable init $static-storage
}
alias init.1
]
It's a bit more expensive than other approaches. But once it's nativized it won't be terrible. It creates an extra variable but only one per static, and since it's static you won't be doing it on every call.
It's slick enough that I'll be using it often in non-performance-critical code!