How To FLATTEN...Anything!

On top of everything else cool about this...

...the part I'm probably most proud of is:

The undecorated code is the correct code.

You don't have to worry about the edge cases that can bite you like in Redbol.

A Study In Contrasts

MAP-EACH is a poster-child for why you need to put splicing intent on the value. Because if you have just MAP-EACH and MAP-EACH/ONLY, then you can't pick on a per-iteration basis whether you'll splice or not.

(Well, if MAP-EACH always spliced, then you could pick not-splicing by wrapping things in blocks...though that's convoluted and wasteful.)

Anyway, Red doesn't have a MAP-EACH of any kind. If we use COLLECT and FOR-EACH, then we get a KEEP that can either splice or not:

red>> flatten: func [block [block!]] [
          collect [foreach item block [
               either block? item [keep item] [keep/only item]
          ]]
      ]

Of course, it's obfuscated. :roll_eyes: You have to know blocks splice by default, and you have to throw that /ONLY in on things you don't want to splice.)

Putting that aside for a moment, it seems to work...at first:

red>> flatten [[a] [b] c d [e f]]
== [a b c d e f]

Yet Red is easily foiled:

red>> flatten reduce [[10 + 20] :reverse [30 [+] 4]]
*** Script Error: either is missing its false-blk argument

Behind the scenes, when item holds an action like REVERSE it acts like that function:

either block? item [keep item] [keep/only item]

; acted like...

either block? reverse [keep reverse] [keep/only reverse]

Thus, you find out that if you were going to be correct you should have written:

red>> flatten: func [block [block!]] [
          collect [foreach item block [
               either block? :item [keep :item] [keep/only :item]
          ]]
      ]

red>> flatten reduce [[10 + 20] :reverse [30 [+] 4]]
== [10 + 20 make action! [[
     {Reverses the order of elements; returns at s...  ; (it's right)

(It was at your discretion whether to say [keep :item] or [keep item]once you knew it was a block, because the : wasn't required... but just to be safe, maybe you should do it all the time?)

This Shows You Just How Far Things Have Come

Putting them side-by-side...

; Redbol

flatten: func [block [block!]] [
    collect [foreach item block [
        either block? :item [keep item] [keep/only :item]
    ]]
]

; Ren-C

flatten: lambda [block [block!]] [
    map-each item block [
        either block? item [spread item] [item]
    ]
]

...you see the triumph of the Isotopic Model. And it only costs one...measly...byte.

:atom_symbol:

From my slides twelve years ago in Montreal:

1 Like