Remix.run Logo
middayc a day ago

Rye is foremost a REBOL and REBOL has this notion of many types of words at it's core:

`word` - regular word, can evaluate to value it's bound to or call a funtion if bound to a function

`word: "value"` - set-word, assignment that you noticed

`probe :word` - get-word, always returns bound value, doesn't call a function if it's bound to a function, in Rye this is `?word`, because `:word` is left-set-word.

`'word` - literal word, evaluates to a word itself

etc ...

Rye adds even more word types. Rye also has left to right flow so it adds left-set-word. In Rye all assigned words with set-words are constants and they are used by default. So we also need a "mod-word", that is the double colon that you noticed, and left-mod-word. Because of left-to-right flow Rye also has .op-words and |pipe-words.

The logic around words, op-words and pipe-words ... I tried to explain here:

https://ryelang.org/meet_rye/specifics/opwords/

Another pattern you noticed (good observation btw:) is the idea of injected blocks that isn't used just for loops, but also for conditionals, function bodies, HOF-like functions etc ...

https://ryelang.org/meet_rye/specifics/injected_blocks/

All in all it is supposed to be a compact set of ideas that fit together. Some are somewhat unusual.

bloaf a day ago | parent [-]

> Rye also has left to right flow so it adds left-set-word. In Rye all assigned words with set-words are constants and they are used by default. So we also need a "mod-word", that is the double colon that you noticed, and left-mod-word

So I would assume that the :i is actually constant within the loop body scope. That is, the loop function is doing something like this:

; i is not assigned in this scope

evaluate {1 :i, prns i}

evaluate {2 :i, prns i}

evaluate {3 :i, prns i}

; i is still not assigned in this scope

But it sounds like you're telling me that :i would actually escape the scope of the loop body and so it needs to be modifiable or else the loop will break.

middayc a day ago | parent [-]

Yes, Rye follows REBOL in this case. Plain block invocation doesn't create it's own scope / context. That holds for do, if, either, loop, for, map, etc.

It would be costly to have this on by default. If you want separation there are many ways to achieve it. Rye has many functions related to contexts / scopes. For creating contexts in multiple ways and evaluating code inside contexts or with context as parent or isolated context, etc.

And a lot of builtins directly accept anonymous functions in place of blocks of code.

For example for loop also accepts function if you want separation and don't mind the cost.

    for { 1 2 3 } fn { x } { print x }
    ; which can also be written with fn1
    for { 1 2 3 } fn1 { .print }
    ; or latest experiment where we have syntax for 3 injected blocks 
    ; .() - same as "with"
    ; .[] - same as "reduce/with"
    ; .{} - same as "fn1"
    ; where it's already decided department from REBOL: 
    ; () is "do"
    ; [] is "vals"
    ; {} is literal block 
    for { 1 2 3 } .{ .print }