New-style slots in io.launcher and smtp
Friday, March 7, 2008
Yesterday I refactored the io.launcher and smtp libraries to use Eduardo’s new-slots and the results were quite nice.
Prior to this, words such as run-process
would take either a string,
an array of strings, or an assoc. The latter was used to specify more
advanced launch parameters, such as I/O redirection. Here is a typical
example of the old way:
{
{ +command+ "make clean install" }
{ +stdout+ "../build-log" }
{ +stderr+ +stdout+ }
} run-process
This looks nice and neat, however, if some of the values are computed, then you cannot use a literal assoc, you have to construct one. Suppose we want to add a 5 minute timeout. Here is one way:
[
"make clean install" +command+ set
"../build-log" +stdout+ set
+stdout+ +stderr+ set
5 minutes +timeout+ set
] { } make run-process
Having to change all of the code just because one parameter becomes computed is not so nice. The other alternative is to use something like bake:
{
{ +command+ "make clean install" }
{ +stdout+ "../build-log" }
{ +stderr+ +stdout+ }
{ +timeout+ %[ 5 minutes ] }
} bake run-process
This is better but now you have bake
, %[
which don’t pertain to your
problem domain, plus some unnecessary Lisp-style nesting. And what if
you wanted to build up a descriptor in one word, but then have another
word which amended it further? You’d have to bake two assocs and use an
assoc word such as ‘union’. Bake is nice in other contexts but it is the
wrong solution here.
Now suppose we used a tuple of process launch parameters:
<process>
"make clean install" over set-process-command
"../build-log" over set-process-stdout
+stdout+ over set-process-stderr
5 minutes over set-process-timeout
run-process
This is more composable; you can write a word which takes a descriptor,
and sets some slots in it. It is also relatively easy to have both
literal launch parameters and computed ones, and adding a computed
parameter to an all-literal descriptor doesn’t require introducing
make-assoc
or bake
. However, now we’ve introduced some stack
shuffles and the accessor names are kind of long.
Enter new-slots:
<process>
"make clean install" >>command
"../build-log" >>stdout
+stdout+ >>stderr
5 minutes >>timeout
run-process
This is about as ideal as it gets. No unnecessary nesting, no stack shuffling, and the only words that appear directly pertain to your problem domain.
What about the case where a launch parameter comes off the stack? Suppose the command name is an input and everything else is computed on the spot. Here is the old way:
[
+command+ set
"../build-log" +stdout+ set
+stdout+ +stderr+ set
5 minutes +timeout+ set
] { } make run-process
The new way introduces a swap, but there is still a net reduction in complexity:
<process>
swap >>command
"../build-log" >>stdout
+stdout+ >>stderr
5 minutes >>timeout
Now suppose we have a make target name on the stack and we want to run make with that target. Here is the old way:
[
"make" swap 2array +command+ set
"../build-log" +stdout+ set
+stdout+ +stderr+ set
5 minutes +timeout+ set
] { } make run-process
With new-slots:
<process>
"make" rot 2array >>command
"../build-log" >>stdout
+stdout+ >>stderr
5 minutes >>timeout
With bake and new-slots:
<process>
swap { "make" , } bake >>command
"../build-log" >>stdout
+stdout+ >>stderr
5 minutes >>timeout
I find the latter preferrable to the first two.
The situation with smtp is a little different. Previously, I had a
send-simple-message
word which took a body, subject, from and to on
the stack:
"Blahblah" "Hi" { "alice@aol.com" "joe@aol.com" } "bob@aol.com" send-simple-message
Clearly, this doesn’t scale as additional parameters are added; header lines, attachments, etc. The new code uses new-slots and it is a lot more aesthetically pleasing:
<email>
"Blahblah" >>body
"Hi" >>subject
{ "alice@aol.com" "joe@aol.com" } >>to
"bob@aol.com" >>from
send
Not only does it read better but also it is easy to add additional fields such as CC, BCC, attachments, S/MIME support, etc. (Well, easy enough to add the fields; actually implementing some of these is another matter altogether…)
New-slots is going into the core very soon now. Bake will need to be worked on some more; then it will go in the core and replace usages of make there. Make will go into a library in extra and be phased out along with old slots.