| Certainly: I can tell the rye devs like the idea of everything being a function. But in their very first "language basics" section, they introduce assignment not as a function call, but as some kind of magic that happens when you have colons in a name. So when we get to the "looping" section, it is the first time we have seen a colon-having-name outside the context of assignment: > loop 3 { ::i , prns i } And it is explained that the above line of code is "injecting" values for the code block to "pick up". But right away this begs a number of questions: * Why the double-colon? I would assume each loop-body-evaluation happens in its own scope, and that we're not creating global variables, so a single colon (:i) should be sufficient, right? * What are we doing, conceptually? Is the ::i meant to be "a function which when given a value modifies its enclosing scope to include the symbol i" or "an unassigned symbol which the loop function will use to do something akin to term-rewriting with?" * Do we really need a symbol at all, or could we just have a the point-free loop "loop 3 {prns}"? * If we can't have the point free thing, is it because somehow the injected value would end up "to the left" of prns, if so, why would we want that? * If we're doing something more like term rewriting, why isn't the symbol given as a separate argument from the body? |
| |
| ▲ | middayc a day ago | parent | next [-] | | 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 }
|
|
| |
| ▲ | kazinator a day ago | parent | prev [-] | | If you treat assignment as a function, then you have to reify environments as run-time objects, whereby you basically lose lexical scope. Lisp originally, as in LISP, had assignment as a function: it was called SET. To use it, you usually had to quote: (SET 'VAR 42). It worked without an environment parameter because variables were in a pervasive environment, but the quote was needed to get the variable symbol as a run-time value. (SET VAR 42)
would mean evaluate VAR to a symbol, and then pass that symbol to the SET function along with 42, so whatever variable was in VAR would be assigned. Assignment is inherently non-functional, since it is a side-effect, so it is mostly counterproductive to model it as a function. A pattern matching or logical language can have implicit bindings as the results of an operation, and so produce variables that way. Then instead of assignment you have shadowing, in that some construct binds a variable again that was already used, so that the most recent value then emerges, shadowing the previous one. | | |
| ▲ | bloaf a day ago | parent [-] | | So tcl handles it somewhat more elegantly, I think. It also has a set function, but does not require any special quoting because it uses the $ prefix to denote "get the value of this symbol": set a 5
puts $a #prints 5
and of course because it is modeled as a function (albeit an impure one) you can pass arguments to it as normal: set a b
set $a 5 #equivalent to set b 5
puts $b #prints 5
of course, everything in tcl is a string, so this works too lol set a 5
set $a b
puts $5 #prints b
| | |
| ▲ | middayc a day ago | parent [-] | | I’ve personally always thought that REBOL’s use of set-words and similar constructs was a strength. It makes sense conceptually, is visually distinguishable, and maintains a strong sense of internal consistency. REBOL (and by extension, Rye) was never designed around the idea that everything must be a function. It just turns out that this approach fits naturally within the core principles and rules of the language. All “active” words happen to be functions, because nothing else is needed. The behavior of different word types (and, more broadly, value types) is determined by the evaluator. In that sense, you could say that Rye does have syntax, expressed through its distinct word types. |
|
|
|