I've complained often about PORT! seeming to try and serve two masters... it tries to act as something of an OBJECT!, but also a stream.
Here is the definition of system.standard.port...the template object from which PORT!s are created:
system.standard.port: make object! [ spec: ' scheme: ' actor: ' awake: ~unset~ state: ' data: ' locals: ' connections: ' ; currently used only by TCP ports ]
That's an OBJECT!, but the underlying datatype is switched to PORT! when the port is created.
How Do You Interact With This Object as an Object?
The "What is a Port" document says this:
Specific action functions can be applied to a port. Some common actions are:
- make - create a new port
- open - initialize the port
- close - finalize the port
- read - read data from port
- write - write data to port
- query - get other information from port
- update - detect external changes to the port
But, there are many other actions as well, as generally defined by Rebol datatypes.
What else qualifies as these "many other actions?"
Rebol2's ODBC suggests PICK (FIRST), INSERT, and COPY as choices.
Source code for File Port in R3-Alpha shows APPEND, DELETE, RENAME, MODIFY, OPEN?, LENGTH?, HEAD, TAIL, NEXT, BACK, SKIP, HEAD?, TAIL?, PAST?, CLEAR.
This is awfully saturated, and it seems nothing is off the table for what this abstract idea of a PORT! might want to react to.
So how do you get at these object fields safely? How do you PICK the SPEC field? How do you POKE the AWAKE function?
More Problems: PORT and 1:1 with STREAM Correspondence
When you're working with a TCP connection, you supposedly think of that as a bidirectional PORT!. You don't open a connection for your input, and one for your output--you read and write to the same port.
If so, why is there system.ports.input and system.ports.output instead of system.ports.stdio?
One technical good reason there is that there are actually two stdio output streams: the stdout and the stderr for error messages. Often these are directed to the same place, but not always.
So here you see that maybe something like stdio would like to be an object aggregating three data streams: 1 for input and two for output.
Difference Between Ports and User Defined Datatypes?
It doesn't really seem like anything is off the table for what you can override on a PORT!.
Can you define what it means to ADD to a PORT!? If not, why not?
What about path selection on a PORT! (or today's field access via TUPLE!, another option).
Once you get into this "anything goes" attitude you are essentially talking about an implementation for user-defined datatypes.
This points out an important aspect of the articulation of any design: You need to be able to say what it isn't, or there's no meaningful definition of what it is.
Streaming/Iterating Is Narrower And Needs a Protocol
While PORT! is slippery, I'm going to be attacking just the more basic questions of streaming and iteration.
We want to be able to say
parse some-100-mb-file ["FOO" <stop>] and not have to read 100 megabytes just to know if it started with "FOO"
And we want to be able to do that with something like FOR-EACH as well...
for-each [x y z] some-100-mb-file [ all [x = #f, y = #o, z = #o] then [break] ]
That shouldn't need to have all 100 megabytes in memory. And a generic solution to this which puts iteration in the mix should allow for streams to be piped and connected to each other to do filtering, encryption/decryption, compression, etc...
So I'm going to focus on the narrower question of how to do that, vs. the muddle of "What is a PORT!", at this time. Wishful thinking isn't going to solve that problem, but rational adaptation of the methods used by other languages that do this might.