Blocks vs Variadics as Dialect Formats


#1

Imagine you have a function that takes one BLOCK! and emits the data from the block to a file by default. But optionally there’s another filename into which the data will be put. The conventional way to do optional arguments in Rebol is via refinements:

emit [x y z]
emit/into [x y z] %foo.txt

The usual suggestion to someone who finds this kind of thing cumbersome might be to consider taking a dialected block:

emit [x y z]
emit [%foo.txt [x y z]]

And if you’re lucky enough to know that the slot where X lives can’t be a FILE!, you might be tempted to eliminate the nested block. But you may not be lucky…and if the block lives in a variable instead of a literal, introducing a new level of block suppresses evaluation and causes you all kinds of grief. You start needing to COMPOSE or REDUCE, and this kind of problem comes up a lot.

With variadics we now have another possibility:

emit [x y z]
emit %foo.txt [x y z]

At the interface level, rather than implementing this using a single VARARGS! it could be supported more cleanly by marking parameters as <skip>able:

 emit: function [file [<skip> file!] block [block!]] [...]

This could tell the evaluator that if it can’t match the type of the first argument, then just set it to void and keep going. An upside to this is that it would mean people using APPLY wouldn’t have to jump any particular hoops to build a VARARGS!, and could supply parameters as normal.

Is permitting this a good idea? Well, the cat’s out of the bag as variadics are permitted now, and I think variadics have many justifiable applications. The real worry is if you start writing things like:

 emit x
 blah-blah

…and the reader can’t be confident if x is a FILE! or a BLOCK!. Of course, if you don’t know if x is a FUNCTION! or a BLOCK! you’ve always had a similar amount of trouble. You also get in a bit of trouble the day you decide that first argument to emit can be either a FILE! or a BLOCK!, still taking a BLOCK! as the second argument. But changing function interfaces always influences callsites, this is just taking it further. I’d argue it’s just how Rebol is.

Now this doesn’t mean it’s a good idea to use for exposing core functionality. But if one is scribbling out a quick-and-dirty script, this freedom may be all part of the deep lake.


#2

I like it, and as it is possible, people will do it.
So having it made clear in the function signature is much cleaner.


#3

Looking quickly at implementation details, there is a problem in the issue of parameter convention. You have to have the parameter gathering convention match the thing you’re rolling over to. Consider:

foo: function [file [<skip> file!] 'word [word!]] [...]

x: does [print "side effect" [a b c]]
foo x

The attempt to fulfill the file parameter won’t quote, it will evaluate… and it cannot undo that evaluation, despite rejecting the argument. Basically the parameter classes must be the same. Soft quotes can only roll over to soft quotes, tight evaluations only to tight evaluations, etc.

Technically speaking, some quotes could roll over into evaluated slots. It’s not totally obvious to see how to write that just now.

You also have to roll over to some required parameter before you run out of arguments. Because otherwise, you can potentially break iterative DO/NEXTs on a block acting the same as DO on that block…since there’s nowhere to hold the rolled-over piece.


#4

Well if you can do a clean implementation, then great. Even if we have to explicitly evaluate variables first to expose their type!


#5

While I haven’t exactly figured out how to write it cleanly without messing up the evaluator loop, it is technically plausible to roll over a quoted argument into a non-quoted argument. Moreover, I actually think this will be the least obscuring case.

The reason it is the least obscuring is because foo blah blah blah blah vs. foo <some-influencing-tag-instruction> blah blah blah blah has the advantage of being clear at the callsite, as opposed to foo mystery-thing blah blah blah blah. Sometimes mystery-thing might be a tag and sometimes it might not.

If I were to be prescriptive, I’d say you should only roll over quoted args, most likely into evaluated ones. If you evaluate you are in a bad situation in the sense that the same code–written with a certain arity assumption–could sometimes skip and sometimes not. It’s hard to imagine code making sense in that case.

Perhaps then <skip> should only be available to hard quotes. But rolling over such hard quotes into subsequent evaluation is not something technically obvious how to do cleanly, so I’ll have to think on it.