HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Interface and implementation of this replacement for Rebol/Red REJOIN

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
thisrebolredinterfaceforrejoinandimplementationreplacement

Problem

This is a draft implementation of a function called COMBINE. It's not intended to be a complex formatting dialect, just a replacement for REJOIN that has a better name and more useful behavior. Its purpose is laid out pretty thoroughly in this blog entry:

http://blog.hostilefork.com/combine-alternative-rebol-red-rejoin/

But to make a long story short, it is supposed to let you flatten expressions into a string. If an expression returns a block, then that block will be recursively evaluated using the same rules as if it had been handed to COMBINE directly:

`combine: function [
{Combine evaluated expressions and substitute block variables to produce a string}
block [block!] "Specification to process"
/with "Add delimiter between values (will be combined if a block)"
delimiter [block! any-string! char!]
/into "Insert output into an existing string instead of making a new one"
out [any-string!]
/local
value
] [
;-- No good heuristic for string size yet
unless into [
out: make string! 10
]

if block? delimiter [
delimiter: combine delimiter
]

needs-delimiter: false
pre-delimit: does [
either needs-delimiter [
out: append out delimiter
] [
needs-delimiter: true? with
]
]

;-- Do evaluation of the block until a non-none evaluation result
;-- is found... or the end of the input is reached.
while [not tail? block] [
set/any 'value do/next block 'block

;-- Blocks are substituted in evaluation, like the recursive nature
;-- of parse rules.

case [
unset? :value [
;-- Ignore unset? (precedent: any, all, compose)
]

none? :value [
;-- Skip all nones
]

any [
function? :value
closure? :value
] [
throw make error! "Evaluation in COMBINE gave f

Solution

Some thoughts:

Initialize String

unless into [
out: make string! 10
]


Don't know how else to discern the initial string allocation, perhaps just start with zero?

OUT might be a strange word for a value that is both input and output. I'd also allow a NONE! value here.

/input out [any-string! none!]


Then you can initialize like:

out: any [out make string! 0]


Delimiter

if block? delimiter [
delimiter: combine delimiter
]


Cool, a delimiter can be combined on the fly!

needs-delimiter: false
pre-delimit: does [
either needs-delimiter [
out: append out delimiter
] [
needs-delimiter: true? with
]
]


Rather than an extra function and an extra value for this, why not just an extra value:

active-delimiter: none


Then set active-delimiter to delimiter after the first step through BLOCK.

Iteration

while [not tail? block] [
set/any 'value do/next block 'block
...
]


I'm not certain about this, but I believe FOREACH is faster than WHILE. This seems like an inefficient way of expressing:

foreach value reduce block [
...
]


Functions

function? :value
closure? :value


Are you just testing for these two types of function? Otherwise ANY-FUNCTION? would catch both.

Throwing Errors

throw make error! "Evaluation in COMBINE gave function/closure"


I don't think this works in Rebol 3, at least not without wrapping the function call in a CATCH block. As I understand it, you need to DO an error to trigger it.

Coerce Output Type

An /AS refinement might be a good way to do this, so long as the type is with the ANY-STRING! typeset. It'd be a close call as to whether /INTO or /AS would take precedence for output type:

type: any [type string!]
out: any [out make type 0]


None/Empty

My opinion here is:

combine []
combine [none]


Should return ""

combine/with [none none] ","


Should return ","

Context

StackExchange Code Review Q#54466, answer score: 2

Revisions (0)

No revisions yet.