Should REDUCE Heed SPREAD?

When suggesting the term SPREAD, @rgchris gave this example for REDUCE behavior, which hadn't occurred to me previously:

>> reduce [spread [a b c] [a b c]]
== [a b c [a b c]]

Nothing about the design makes this happen automatically. REDUCE has historically errored on isotopes. So code has to be added to override that.

I'm guessing most people would be in favor of having the splicing behavior. Arguments that say that there should be a 1:1 correspondence between expressions and values in a REDUCE are already pretty much out the window, since VOID elements vanish (including conditionals that don't take any branch).

DELIMIT Would Presumably Want It Too

I've complained in the past that the often random-seeming treatments of blocks in Rebol2 functions like REJOIN lead to problems--and that it would be better if people had to be explicit about their intent. This offers the ability to "inherit" whatever the enclosing delimiting strategy is, and fold into the existing operation (technically more efficient):

>> block: ["c" "d"]

>> spaced ["a" "b" block]
** Error: BLOCK! not handled by DELIMIT, use SPREAD or desired string conversion

>> spaced ["a" "b" spread block]
== "a b c d"

>> spaced ["a" "b" unspaced block]  ; if you wanted another interpretation
== "a b cd"

I believe I prefer this over having some default way that blocks behave inside string conversions. The odds of guessing right are low enough that it's better to have people be explicit.

So...presuming there's no objections....there's an agenda item to fuse together the stackless logic that performs a REDUCE so that it's the same code running in DELIMIT, so I'd probably go ahead and do that and make these things work.

UPDATE circa Oct 2022: This has been done, so you can now use this feature.

4 Likes

A small thought for now:

a: 'a b: 'b c: 'c
; for infinite reduction of 'a 'b and 'c

block: [a b c [a b c]]

reduce block
=> [a b c [a b c]]
; ok

find block spread pick block 4
=> [a b c [a b c]]
; ok

spread pick block 4
=> isotope value
; ok, or 'isotoped value' or 'isotopic value' or just 'isotope'?

reduce block
=> [a b c a b c]
; ??

Does SPREAD here apply the isotope to that value in place or does it create something new? I kind of suspect the latter, but is it reasonable to expect the above behaviour?

It creates something new.

It's reasonable to ask if it works that way. But it does not. :slight_smile:

The quoting level of a BLOCK! (or any other value) lives in its cell, not in the series pointed to that contains the contents. It's a property like whether a slot in a block has the NEW-LINE flag.

Remember that the same underlying array storage can be imaged multiple times:

>> block: [a b]

>> four: reduce [block, quote block, quote quote block, quasi block]
== [[a b] '[a b] ''[a b] ~[a b]~]

>> append block 'c

>> four
== [[a b c] '[a b c] ''[a b c] ~[a b c]~]

The quoting (and/or quasi) level lives resident in the bits of FOUR's array holding the cells. The cells point at the array storage for BLOCK.

The only way to change the quoting levels is to manipulate the actual contents of FOUR. You can't do it by modifying the BLOCK variable to change its quote level--that's only relevant to the cell stored in the context where block's value is looked up:

>> block: spread block
== ~(a b c)~  ; isotope

>> four
== [[a b c] '[a b c] ''[a b c] ~[a b c]~]  ; unchanged

Hence there's no means to subversively create the situation of an isotope inside a block remotely. You have to go through a function like CHANGE. They can spread the values, or error, or have some other reaction--but they'll never be allowed to put the isotope in the block.

2 Likes

I think I've mentioned before that this raises some questions about how casual we should be about variables holding isotopes.

We already know that true and false isotopes, as well as null isotopes, have to be able to be accessed casually. How about splices?

For instance, should you be able to write this:

>> x: spread [a b c]

>> reduce [x x]
== [a b c a b c]

Should the assignment itself create an error without a SET/ANY?

>> x: spread [a b c]
** Error: Can't assign isotopes without SET/ANY

>> set/any 'x spread [a b c]
== ~(a b c)~

Or should you need to use a special form of access, like GET-WORD!?

>> reduce [x x]
** Error: X contains a splice isotope, access with GET-WORD!

>> reduce [:x :x]
== [a b c a b c]

At this point I think I'm leaning to saying the plain access works. Remember this old behavor:

rebol2>> x: [a b c]

rebol2>> append [a b c] x
== [a b c a b c]

Looked at that way, it's kind of like you were "appending a splicing isotope through a plain reference".

2 Likes