What were Rebol2 Ports Like?

The What is a PORT! post says something that is often said in things written by (presumably) Carl:

"In Rebol 2, ports were built on a pure series model. However, we found this approach to be problematic because ports are not pure series. They also embody state (information)."

"Rebol 3 moves away from the pure series model of Rebol 2 and more toward an I/O stream model. Now it is closer to the concept found in other programming environments and languages."

The pure series model is gone. Ports are more pragmatic now, and this has resulted in a port system that is cleaner, smaller, faster, and more extensible than ever before.

But that doesn't seem to be people's experience. I haven't heard this newfound "smaller/faster/more-extensible" pleasing anyone in particular. Mostly just people complain that they didn't have the ports they used to have.

My questions:

  • What about Rebol2 port code made it a "pure series"? You couldn't PARSE a PORT! in Rebol2, I don't think. How was it "pure" or "impure"?

  • Is there any case of something that didn't work in Rebol2 that was suddenly able to work in R3-Alpha? e.g. an example of Rebol2 code where the R3-Alpha is better?

  • Does anyone have an example of going backwards? Where Rebol2 code was good and R3-Alpha seems wrong?

cc: @gchiu @rgchris @Brett

There was much more documentation about the r3 ports than for rebol 2. I wouldn't have attempted a r2 port due to this. But I was able to do all the protocols that were present in r2 with just a little help.

Also, r3 was asynchronous so that allowed the GUI to work unblocked. Whereas in r2 any network activity blocked the GUI.

But due to asynch nature some things are more complex.

I do find the Rebol 3 port interface to be much cleaner. Rebol 2 ports made a lot of assumptions in their design about a scheme being specifically a stream of bytes from somewhere and contained a lot of hidden state mechanics that had to be managed:

SYSTEM/STANDARD/PORT is an object of value: 
   scheme          none!     none 
   host            none!     none 
   port-id         none!     none 
   user            none!     none 
   pass            none!     none 
   target          none!     none 
   path            none!     none 
   proxy           none!     none 
   access          none!     none 
   allow           none!     none 
   buffer-size     none!     none 
   limit           none!     none 
   handler         none!     none 
   status          none!     none 
   size            none!     none 
   date            none!     none 
   url             none!     none 
   sub-port        none!     none 
   locals          none!     none 
   state           none!     none 
   timeout         none!     none 
   local-ip        none!     none 
   local-service   none!     none 
   remote-service  none!     none 
   last-remote-service none! none 
   direction       none!     none 
   key             none!     none 
   strength        none!     none 
   algorithm       none!     none 
   block-chaining  none!     none 
   init-vector     none!     none 
   padding         none!     none 
   async-modes     none!     none 
   remote-ip       none!     none 
   local-port      none!     none 
   remote-port     none!     none 
   backlog         none!     none 
   device          none!     none 
   speed           none!     none 
   data-bits       none!     none 
   parity          none!     none 
   stop-bits       none!     none 
   rts-cts         logic!    true 
   user-data       none!     none 
   awake           none!     none

This didn't really make much sense where a scheme did not behave in this way. Pretty much any secondary scheme (that is, one built on top of a lower-level scheme—TCP, FILE, e.g. HTTP, MYSQL, S3) is likely more abstract.

"pure series" ?

I had the idea once that I could make an in-memory port that would return a calculated series just-in-time - like some languages have iterators. Perhaps it would be some incremental dialect thing. But ports are assumed to be tied to external/network activity - so that was too heavyweight for the ideas.

Why not just use an object for that? I was just playing. A scheme offered a single "class" to put the definition while opening "multiple instances". It was an object that not only can primarily receive block messages but also reacts to control events like being opened and closed.

Finalisation...

I wrote a Rebol2 scheme called DO-POP for manipulating mailboxes via POP. According to the documentation I said I used a port because a port will receive a close event when Rebol shuts down. I don't think such a finalisation event was available anywhere else in Rebol2. This event provided me the opportunity to issue a graceful quit to the server even if the client script/user quit without closing the port. It's always been an annoyance to me that a finalisation event isn't available generally in Rebol.

Writing a scheme for R3-alpha was just as difficult as writing for Rebol2 because in both cases I don't think the concepts had been refined and documented properly - too much reverse engineering and sherlock holmesing… Pushing data through is one thing, then there's the control aspect.

2 Likes

The other possible advantage for r3 was that downloads can be sent to disk, but all r2 reads were in memory which caused memory issues for large files. However, Anton Rolls did do a large file downloader for r2.