In R3-Alpha, there was the idea that FUNC was lower-level, and "FUNCTION" was built on top of it.
Its principal difference was automatically collecting SET-WORD!s as locals. But it added two refinements: /WITH and /EXTERN.
/EXTERN was a way of saying what shouldn't get collected as a local variable due to being a SET-WORD!:
global-var: 10
foo: function/extern [arg1 arg2] [
local-var: "hi"
global-var: 20 ; /EXTERN protected this from being collected local
return arg1 + arg2
] [global-var]
/WITH was a way of making static variables:
accumulate: function/with [x] [
return state: state + x
] [state: 0]
Early On, Ren-C Moved Everything Into The Spec
I didn't like seeing the refinement arguments at the end (of what could be a very long function definition).
It didn't occur to me to suggest that refinement arguments be moved to the head (which they probably should). But what did occur to me was that the function spec could incorporate these properties.
I actually thought <with>
seemed better than extern, as a nicer word for "Use these existing variables". And <static>
seemed like a well-known term for static variables:
foo: function [arg1 arg2 <with> global-var] [
local-var: "hi"
global-var: 20
return arg1 + arg2
]
accumulate: function [x <static> state (0)] [
return state: state + x
]
The subtlety of wanting to use an object instance was also added, as <in>
:
obj: make object! [a: 10 b: 20]
bar: function [x <in> obj] [
return a + b + x
]
To my eyes, that all seemed like improvement.
But it didn't come without cost: The spec had to be transformed into something the lower-level FUNC could understand.
This meant there was a layer of parsing and production of a new spec that was a tax on every function creation.
Time Passes, SET-WORD! Gathering Is Panned
It didn't take long for me to decide that SET-WORD! locals-gathering was bad... a gimmick that only made sense in very limited domains (perhaps code-golf)
This motivated having an answer for how to implement LET, as "virtual binding" became the new plan.
Once that transition went through, the effect of <with>
was to become commentary. Since all it did was remove SET-WORD!s from the collection list, and there was no collection any longer.
Though virtual binding did open up a new possibility, that if your block had a different binding than the spec, then the WITH might import visibility of terms to that block:
global-variable: 10
block: /get-block-from-somewhere ... ; doesn't know about GLOBAL-VARIABLE
/foo: function [x <with> global-variable] block
But this would be a binding operation, that is better generalized as:
/foo: function [x] (bind @global-variable block)
Pushing The Features To BIND Make The Most Sense
Not just <with>
, but the <static>
and <in>
features seemed to be better as BIND operations as well.
The static syntax of not using SET-WORD!s was based on the idea that SET-WORD!s were reserved for local variables (and RETURN: syntax). So it was a WORD! followed by a GROUP! to initialize.
I think it's better done with just BIND to a FENCE!
accumulate: function [
x
] bind {state: 0} [
return state: state + x
]
<in>
is similar.
obj: make object! [a: 10 b: 20]
bar: function [
x
] bind obj [
return a + b + x
]
Uglier? Maybe. More General? Yes. Faster? Definitely.
Feature-wise, there's a slight loss of the commentary capacity of <with>
.
global-var: 10
foo: function [arg1 arg2 <with> global-var] [ ; no-op, but useful?
global-var: 20
return arg1 + arg2
]
But besides that, there's no loss of features to move everything to a BIND operation on the body.
Not having to PARSE the spec and generate a whole new one is a big performance win.
So I'm letting go of those features.