Input/Output Ports
I'd add here that input/output may not be the same end point. In Rebol 2, as I understand it, in a console session both (SYSTEM/PORTS/) INPUT and OUTPUT were set to the same CONSOLE scheme, though when called from the shell, it uses a FILE scheme. Somewhere lost in the translation to Rebol 3 is in part why Rebol 3 was incomplete (and where Rebol 2 still has flaws):
My primary usage of Rebol is running scripts from the shell and such usage is constantly polluted with artifacts from implied terminal usage (the dreaded >>
or the 5... 4... 3... 2... 1...
bomb of R3C-era Ren-C.
Anyways, while it's somewhat intuitive that INPUT/OUTPUT are set to the same scheme, it doesn't necessarily have to be. You could set output to another open FILE port and all output is redirected there. I'm not entirely certain what the merits of any effective exploitation of this would be, but IDK.
What is a Port?
I don't know that this necessarily follows. I've argued that ports are as close as you get to user-defined datatypes, but I'd also suggest that this is a misapplication. I think that PORT exists (as the name implies) as an interface to an external system or construct. When viewed this way, you might also consider SERIES or MAP to be internal ports with a fixed and native implementation of the, let's call them, say, 'manipulation verbs' and while it can be desirable to have those verbs be consistent with custom ports, it doesn't necessarily have to be so. It depends on context.
I think this definition tracks with the use of URLs/FILEs as the initiator as well.
Iterators
Javascript handles iterators in much the same way Rebol handles ports: objects that conform to a set of conventions that integrate them with fixed language constructs (e.g. for...of
). The result of this is that you can layer things in customisable ways. Rather than explain this in Javascript, I'll offer a prospective way for how this might look as Rebol.
Test Case
I have a little test case for where a piece of data is stored in a few layers of formats:
encoded: "start F3Dl7!! end"
This snippet contains the string 'Foo' where it is Deflated and then encoded as Ascii85. It might be possible just to copy text between 'start' and 'end' and apply the decoders sequentially, however "end"
is valid Ascii85 and spaces are permitted, so the following may stymie this strategy:
encoded: "start F3DI7!! end end"
Ascii85 is not self-terminating, Deflate is. Thus a way to approach this might be to use an iterator for each level. Prospective solution:
encoded: make string/iterator [
source: "start F3DI7!! end"
]
encoded/consume "start"
encoded-ascii85: make ascii85/iterator [
source: encoded
]
encoded-deflate: make deflate/iterator [
source: encoded-ascii85
]
result: make binary! collect [
while [
byte: encoded-deflate/next
][
keep byte
]
]
encoded/consume " end"
=> true
Could just as easily begin:
encoded: make big-file/iterator [
source: %file-containing-data
]
Like ports, this still doesn't give you consistency between formats—what does it mean for a JSON iterator when you say:
json-iterator: make json/iterator [
source: "[1, 2, 3, 4]"
]
probe json-iterator/next
=> one of:
== [1 2 3 4]
== [open-array] ; to be followed by [number 1]
I'm not exactly certain how one would go about making these iterators in an efficient fashion. Pulling byte by byte from a Deflate iterator might be a slow way of doing that.