Being Blunt And Obvious With PRINT

I just simplified the implementation for PRINT, using much less trickery and just being blunt.

print: procedure [
    "Output processed value to STDOUT, with newline if any text is output"

    value [
        <opt> "no output (including no newline)"
        text! "output text, empty string still outputs newline"
        block! "uses SPACED on block (for no spaces, use PRINT UNSPACED [...])"
        newline? "output just the single newline"
    ]
][
    case [
        not value [
            noop
        ]
        text? value [
            write stdout value
            write stdout newline
        ]
        block? value [
            if value: spaced value [
                write stdout value
                write stdout newline
            ]
        ]
        newline? value [
            write stdout newline
        ]
        ~(unreachable)~
    ]
]

Some Notable Aspects

  • This uses PROCEDURE so that it doesn't have to have return: [trash!] in the spec.

  • The VALUE argument has TEXT! documentation for each type in the type spec embedded in the type spec block, vs. trying to write a single string that explains the reactions to the types.

  • It uses NOOP instead of leaving the code block empty, while having the same effect. This helps make everything look intentional.

  • The ~(unreachable)~ "hot potato" is a light way of making the CASE error out if it unexpectedly doesn't match any of the conditions.

If You've Seen PRINT In The Past, This May Surprise

PRINT used to be more complex, including things like:

 (write stdout cond spaced line) then [
     write stdout newline
 ]

That handles both TEXT! and BLOCK! (because SPACED passes thru text).

But is this cleverness really worth it? It feels like probably not.

Boring and obvious code carries benefits. I think if someone digs into the source for PRINT and finds "oh, I can read that!" it's a better place to start.

1 Like

Returning exclusively TRASH! is a simplifying decision, in and of itself.

It meant dropping a PRINT feature, which was returning NULL if the SPACED product vaporized fully:

old>> print ["" ""]
                    ; <-- PRINT printed this newline
                    ; <-- the console outputs this newline between commands
old>> print [() ()]
== \~null~\  ; antiform

old>> print [() ()] else [print "This was the idea"]
This was the idea

While return: [trash!] is a kind of disturbing thing to see in the spec of a foundational function, return: [<null> trash!] would require even more explanation and hand-waving.

I realized we could do even better, if PRINT accepted VOID... and pushing the behavior elsewhere.

 print spaced [() ()] else [
     "this is the new idea"
 ]

Now the ELSE is reacting to the SPACED, not the PRINT. Not only can you run arbitrary code, you can say what to print instead... or you can say to print nothing!

 print spaced [() ()] else [
     <your arbitrary code here>
     none
 ]

Once the simplifying stroke of moving from [<null> trash!] to [trash!] was made, then PROCEDURE could be used instead of FUNCTION... which gets rid of the return spec, thus sweeping the "TRASH" terminology under the rug a bit.

Note that you can still get PRINT to "return NULL" with veto-ing, but you have to call SPACED yourself:

>> name: null

>> print cond spaced [() ()]
== \~null~\  ; antiform (logic!)

This is a good example of the parts coming together in the name of simplicity, yet still being amazingly expressive and powerful despite that.

1 Like