Should END-able constructs all use ^META parameters?

R3-Alpha and Rebol2 could only have functions that were "endable" if the argument they took was quoted. This feature was added primarily for HELP, so that you could say either:

>> help
; (would display generic HELP usage information)

>> help topic
; (would display help for the given topic)

It was a very limited form of variadic-ness...generally used only in console-oriented commands (HELP, LS). You couldn't write a function that was evaluative, like:

redbol>> printer 1 + 2
3

redbol>> printer
You called the PRINTER function with no arguments
    ; ^-- not possible to accomplish with an otherwise evaluative argument!

Being able to handle getting to the end of input was entwined with taking quoted arguments.

Ren-C added <end>-able Evaluative Parameters

To facilitate certain demos in Ren Garden, Ren-C could mark an ordinary parameter as being <end>-able. This would mean that the argument would show up as being NULL if the end was reached before an argument was seen.

This was--however--ambiguous with if you actually passed an evaluative NULL.

ren-c>> printer 1 + 2
3

ren-c>> printer
You called the PRINTER function with no arguments

ren-c>> printer null  
You called the PRINTER function with no arguments  ; d'oh

This kind of ambiguity wasn't new...the Redbol version had it. The signal for quoted parameters that were endable-and-missing was to make the parameter an UNSET!. Which meant they couldn't tell the difference between help #[unset!] and just-plain-help:

red>> help #[unset!]
To use HELP, supply a word or value as its
argument:

    help insert
    help system
    help system/script

To view all words that...

Interestingly enough, Ren-C has a solution for this with quoted parameters, because NULL cannot appear literally in source...so it can't be at the callsite. Thus NULL can represent a missing quoted argument. Which is neat.

^META parameters can do <end> another way...

A meta parameter is quoted, but will be a quoted void if the callsite was passing a void:

So if our PRINTER took a ^META argument:

>> printer 1 + 2
3
; (it actually received '3, quoted)

>> printer '
You called the PRINTER function with no arguments
; (it actually received ')

The ambiguity is still there, though...

>> printer void
You called the PRINTER function with no arguments
; (again, it actually received ')

But at least you could differentiate NULL from an end. The conflation of an invisible argument with the end doesn't seem as troubling to me, as the problem with HELP is fixed since it quotes and can tell when you say help void vs. plain help

Killing off <end> as a core parameter flag would simplify things...

  • If a quoted parameter tolerates NULL as one of its legal types that's sufficient to say it is "endable"

  • If an evaluative parameter needs to detect endability, it could be your job to make it a ^META parameter and look for void, and unquote it to handle other results.

The code and typeset flags for <end> could then be scrapped.

If someone really liked the NULL conflating version of endability they could write something to do it in usermode.

You'd have to see the code to understand why I would think throwing away <end> is worth it. The way the type checking is done frames have to be filled first, which means if a function doesn't want an actual null but wants just ends to reflect as null... or wants an actual null but doesn't want ends reflected as null... hidden bits need to be grafted onto these nulls at the time of frame fulfillment to say whether it's an "endish" null or a regular null. Various parts of the system then need to test a NULL for this invisible property. ^META parameters pull such invisible state into the light.

Basically take my word for it: meta is much cleaner, and offers a way to expose these distinctions to the user--so I think the odds are that <end> and its current mechanics need to die.

3 Likes

Trying to do this creates an unappealing situation, because it means all ^META parameters start accepting end conditions by default. :frowning:

That's a bit of an overreach. Just because you say you want an argument to be passed via meta protocol, having to handle the ~void~ case of there being nothing there becomes a hassle. Labeling things <end> is much more convenient, otherwise you will end up with a lot of accidental tolerance of ends...producing particularly troublemaking invisibility signals.

Nevertheless, I think we can say that if you care about the difference between an end and an evaluated NULL then we now have an answer: an endable ^META parameter which receives ~void~ can only get that if at an end. With what I observed about ^(...) I think we can say there are no "supermeta" parameters... so the only way you will get a ~void~ indication will be if the evaulation reaches an END (or a comma).

So "endish nulls" are dying, which should get rid of bugs like this:

2 Likes

...but now...there is a solution!

I've mentioned how we're now at a point where when you ^META things you always get something that's either QUOTED! or QUASI!, because the edges of VOID and NULL are now spoken for

>> meta null
== '

>> meta void
== ~

That leaves a ton of representations open for what an <end> could be... but of course the best one jumps out at us right away... NULL !

NULL is now a viable ^META answer for "I couldn't even get you that meta argument you wanted"! And it has just the right amount of orneryness. Easy to check for, and you won't likely be passing it on to places

With that issue solved, we can come back to the original question:

Should END-able constructs all use ^META parameters?

I have reasons beyond the scope of this post, but:

  • I think you should be able to make any parameter endable

  • It's probably best if NULL is used to represent that across the board.

  • This doesn't create conflation in the case of quoted arguments (you can't quote a NULL because nulls can't exist in the input array or variadic feed)

  • There won't be conflation for typed normal parameters, like arg [word! integer! <end>]

    • If the conflation bothers you then you are officially a fancy-pants client... use ^arg
2 Likes

This was the right spirit, but given some further thought I am proposing to go ahead and reserve the isotopic word of ~end~ for this purpose (and in general, go ahead and probably reserve all isotopic words for system use)

If your argument is endable and not ^META, it will be something like this.

>> foo
Congratulations, you got an ~end~ isotope

>> foo ~end~
** Error: ~end~ is not a legitimate callsite argument

>> f: make frame! :foo
== make frame! [value: ~]

>> f.value: ~end~
>> do f
Congratulations, you got an ~end~ isotope

If your argument is ^META and not endable, you can get this

>> bar
** Error: bar.value is not endable

>> bar ~end~
Congratulations, you got an ~end~ quasiform

>> f: make frame! :bar
== make frame! [value: ~]

>> f.value: ~end~
>> do f
** Error: bar.value is not endable

>> f: make frame! :bar
== make frame! [value: ~]

>> f.value: '~end~
>> do f
Congratulations, you got an ~end~ quasiform

And if you have an endable ^META parameter, it meshes both possibilities together:

>> baz
Congratulations, you got an ~end~ isotope

>> baz ~end~
Congratulations, you got an ~end~ quasiform

>> f: make frame! :baz
== make frame! [value: ~]

>> f.value: ~end~
>> do f
Congratulations, you got an ~end~ isotope

>> f: make frame! :baz
== make frame! [value: ~]

>> f.value: '~end~
>> do f
Congratulations, you got an ~end~ quasiform

...under these rules there is no conflation...

Functions only receive ~end~ isotopes on <end>-able parameters, but those isotopes can come from deliberate assignment in frames as well as an end encountered at the callsite.

1 Like