What if DELIMIT and friends treated @... as MOLD requests?

Building on the concept of "@ means do not splice"... what if a literal usage of @... in a PRINT rule would mold the referenced thing, while the default would be to FORM it?

>> items: ["alpha" "beta" "gamma"]

>> print ["Without:" items]
Without: alpha beta gamma

>> print ["With:" @items]
With: ["alpha" "beta" "gamma"]

There's a kind of parity here--with the proposal to have @ be modal parameter control meaning "/ONLY, no splicing". And it goes along with the concept of wanting to call these LIT-WORD!, LIT-BLOCK!, LIT-GROUP!, etc... it's the "more literal way of handling things". And it goes along with the non-reducing behavior of the type in the evaluator (only modal parameters trigger evaluation).

This isn't the first time I've suggested having some dialected concept for this. There were some experiments in the ancestral precursor to today's SPACED (which was called COMBINE). It was a bit of a testbed for some ideas...the main one of which we take for granted today is that it would vaporize failed conditionals completely.

But when I'd suggested it before, I was suggesting things like LIT-WORD! which seemed to undermine the evaluator. (e.g. why wouldn't print ['hello] just print hello).

This seems neat to me. And maybe with it, we would worry less about what MOLD is called (I still don't want to call it mold, though!).

2 Likes

I like this idea too. More power and conciseness for generating output. (rant) I dislike having to iterate over a block (sometimes more than once) to get its composition/order/format just right before I can print/rejoin it for output. E.g., Given a block of items where you wish to interleave/sandwich each with formatting/tags except for the last position. You either have to use a multiline while statement on the block to check for the tail, or use a for-each paired with a line to chop off the uneeded vestige. (/rant)

Can you give a concrete example? DELIMIT is designed to handle the most common instance in this category.

>> items: ["alpha" "beta" "gamma"]
== ["alpha" "beta" "gamma"]

>> print [{Your items were:} delimit {, } items] 
Your items were: alpha, beta, gamma

The question of knowing during an iteration (like a MAP-EACH or FOR-EACH) if you are on the first or last item or not is something I've thought about. It's valuable to know. The general answer right now is that you can't know unless you're using a FOR-NEXT so you have a position to check:

for-next x ["a" "b" "c"] [
    if head? x [print "at head"]
    -- (first x)
    if last? x [print "at tail"]
]

That gives:

at head
-- (first x): "a"
-- (first x): "b"
-- (first x): "c"
at tail

You can certainly combine that with a COLLECT and get what you might want.

>> items: ["alpha" "beta" "gamma"]
== ["alpha" "beta" "gamma"]

>> print [{Your items were:} collect [
     for-next x items [
         either last? x [
              keep/only x/1
         ] [
              keep reduce [<hi> x/1 </hi>] 
         ]
  ]]]
  Your items were: <hi> alpha </hi> <hi> beta </hi> gamma

I feel practical examples can help guide us to understand if those spaces should be in there or not. Just needs some thought...

(Note: Today's code wouldn't require the /ONLY on the KEEP for the single element, but I want everyone looking at these examples in light of my strongly-worded suggestion that it be required if not a block. Though...I just started wondering if the same way that PRINT accepts strings, if you accepted strings as the only exception that didn't require /ONLY for blocks, if it would maybe help pick up on code that's trying to be generic vs. not...)

1 Like

I didn't have a moment to dig one up-- it is just something that comes up regularly and sticks in my craw when I'm generating reports, mainly consolidating multiple rows of into a single row for a csv/spreadsheet. I'd typically define a function:

interleave: func [blk [series!] item] [
	pos: next blk 
	while [not tail? pos] [
 		pos: next (insert pos item)
 	] 
 	return blk
 ]

Then later in the script...

parse <XML for list of sub-elms or html for attrs; append to a block attr-blk>...
print rejoin interleave attr-blk ", " (or add to a big string for writing to a .tsv)

It's an irritant to need a multiline fn just to avoid having a ", " at the end of the list of data. Of course, I probably need to just update my scripting practices with the latest tricks.

1 Like

Further thought: while I have mused that print @[...] might be a way of saying "unspaced"...that seems random compared to print @x being a synonym for print mold x, if print ["The value is:" @x] is interpreted as print ["The value is:" mold x].

e.g. the @ would give you a kind of print/only functionality, allowing you to subvert the rule that plain PRINT only accepts TEXT! and BLOCK! (with BLANK! opting out and the newline char meaning just newline...not two newlines).

There's nothing particularly illiterate about print unspaced [...], so abbreviating it may not be necessary.

interleave is something that should probably be in the box, it does come up a lot.

Though really, I feel that Rebol needs a lookover from someone who works regularly with higher order functions. I've actually wondered if we might even bias to functions...e.g. to have skippable parameters for the "vars" of the iterations:

>> for-each [1 2 3] :print
1
2
3

>> x: [1 2 3]
>> for-each x :print
1
2
3

>> for-each @x [1 2 3] [print x]  ; @x is <skip> arg, hard quoted
1
2
3

>> for-each [1 2 3] func [x] [print x + 1]
2
3
4

With lambda notation:

>> for-each [1 2 3] (x => print x + 1)
2
3
4

>> for-each [1 2 3] x => [print x + 1]
2
3
4

At the cost of one character, it seems like we get benefit. With @(...) you could still evaluate your words as per soft quote.

>> for-each @(second [x y]) [1 2 3] [print y + 1]
2
3
4

It seems a small price to pay to curry favor (pun intended) with the FP people. Note I have a plan such that parameters are picked up implicitly, so that this will work:

>> for-each [1 2 3] (=> print)
1
2
3

As well as something like this:

>> block: [a b c]
>> for-each [1 2 3] (=> append block)
>> block
== [a b c 1 2 3]

Or even this:

>> block1: [a b c]
>> block2: [1 2 3]

>> for-each reduce [block1 block2] (=> append _ <magic!>)

>> block1
== [a b c <magic!>]

>> block2
== [1 2 3 <magic!>]

What's "new" in all this is just how much userspace fun people will be able to have. I think it can open these concepts up to people who would be intimidated otherwise, like trying to do this stuff in Haskell. It won't be pure, but if your problems aren't pure then that can be an asset.

And I kind of have an idea, in the spirit of how CONST is working, for how PURE could work...for some things. It wouldn't a-priori catch a violation, but you'd get an error if you broke the rule.

2 Likes