I've mentioned that binding might be helped by a dialect, and it turns out there is some precedent in hiiamboris's WITH:
USAGE SUMMARY
with x [..] == bind [..] x with 'x [..] == bind [..] 'x with :fun [..] == bind [..] :fun with [:fun] [..] == bind [..] :fun with [x y 'z] [..] == bind bind bind [..] 'z y x with [x: 1 y: 2] [..] == bind [..] context [x: 1 y: 2]
EXAMPLES
omit the path to an object, but work inside it's context:
do with face/parent/pane/1 [ color: red text: mold color visible?: yes ] if true with system/view/fonts [print [serif size]] f: func [/- /+ /*] [ ;-- redefines important globals locally (do something with local flags) foreach x [set..] with system/words [ (do something with global * + -) ] ]
create static storage for functions where existing literal forms don't allow you to:
factorial: func [x] with [cache: make hash! [0 1]] [ any [ select/skip cache x 2 put cache x x * factorial x - 1 ] ]
anonymize words used during initialization of the program:
first item in the block should be of set-word! type do with [x: 1 y: 2] [ z: x * y ... other code that uses x or y ... ]
bind a block to multiple contexts at once (in the list order):
First item in the block should be of word!/get-word!, path!/get-path! or lit-word! type
words and paths values are fetched, while lit-words are converted into words
get-words and get-paths should be used for function context, otherwise they get evaluatedif resulting value is a context, block is bound to it
if resulting value is a word, block is bound to the context of this wordthe following example illustrates usage of words and lit-words:
a: b: x: y: none c: context [ a: 1 b: 2 f: func [x y] [ ; calls `with` internally print composite [self 'x] "a=(a) b=(b) x*y=(x * y)" ; equivalent print composite [self :f] "a=(a) b=(b) x*y=(x * y)" ] ]
Thus,
with [c]
is equivalent towith c
, whilewith ['c]
- towith 'c
.WHY IS IT DESIGNED LIKE THIS?
It does not evaluate
with
does not evaluate the block, so:
- it can be used after
context
s,if
s,loop
s,func
s, etc.- it can be chained
with x with y ...
I've found that this makes code much more readable than it would be with
bind
.
Prefix it withdo
if you want immediate evaluation.It accepts blocks
Design question here was - if we allow block! for
ctx
, how should we treat it?
convert it to a context?
ctx: context ctx
that shortens the
with context [locals...] [code]
idiomlist multiple contexts in a block as a sequence and bind to each one?
that shortens
with this with that [code]
idiomPersonally, I've used the 1st at least a few times, but 2nd - never, though I admit there are use cases.
This can be solved by checking type of the 1st item in the block is a set-word or not
But still ambiguous! Whenwith
gets aword!
argument it can:
- get the value of this word, which should be an
object!
, and bind to this object- get the context of this word, and bind to this context
When inside a context, 2nd option is nice:
context [ a: 1 f: func [x y] [ with [self x] [x * y * a] ] ]
..where the alternative would be:
context [ a: 1 f: func [x y] [ with context? 'x with self [x * y * a] ] ]
When outside of it, 1st option is better:
x: context [x: 10] y: context [y: 20] do with [x y] [x * y]
..where the alternative would be:
x: context [x: 10] y: context [y: 20] do with in x 'x with in y 'y [x * y]
But this still can be solved: let
word!
s evaluate to contexts andlit-word!
s, same as we havebind code ctx
vsbind code 'ctx
:context [ a: 1 f: func [x y] [ with [self 'x] [x * y * a] ] ] x: context [x: 10] y: context [y: 20] do with [x y] [x * y]