Analogue to Rebol2's Hidden Parameters

Rebol2 had the property that any refinements after a local would not show up in HELP...but still be available:

rebol2>> foo: func [arg /local loc /bar baz] [
    print ["Unsurprisingly:" arg]
    if bar [print ["Surprise:" baz]]
]

rebol2>> help foo
USAGE:
    FOO arg

DESCRIPTION:
    (undocumented)
    FOO is a function value.
                           ; <-- look, no /BAR...
ARGUMENTS:
     arg -- (Type: any)

rebol2>> foo/bar "Ordinary argument." "Hidden refinement!"
Unsurprisingly: Ordinary argument.
Surprise: Hidden refinement!

It's actually just exploiting the fact that HELP stops printing out everything after /LOCAL in the spec block.

While it may seem like a loophole that gave rise to a useless feature, there actually is a reasonable use case for functions having out-of-band arguments.

The use case is when a function recurses, and wants to pass some private state into the recursion. If you try to do this in C or some other language, you have to write two functions with two different interfaces. Simplistic example:

fiveprint-core: func [value depth] [
    print [value]
        if depth <> 0 [
            fiveprint-core value (depth - 1)
        ]
    ]

fiveprint: func [
    {Print a value five times (but use recursion to do it...)}
    value
][
    fiveprint-core value 5
]

Having a way to slip in hidden state gives you another option, and it's an option that doesn't make you replicate the interface twice. This FIVEPRINT only takes one argument, but imagine if it had ten arguments with types and descriptions...and it's very helpful to not have to repeat that in a second function with a few extra internal parameters:

fiveprint: func [
    {Print a value five times (but use recursion to do it...)}
    value
    /local /recurse depth
][
     either recurse [
         if depth <> 0 [
             print [value]
             fiveprint/recurse value (depth - 1)
         ]
     ][
         fiveprint/recurse value 5
     ]
]

Ren-C Has Light-Years More Tools, but How To Apply Them Here?

If we didn't clear local variables during tail-calls, you could slip state to a recursion that way. I'm not crazy about the idea of having locals be anything but null at the outset of any call, though.

Maybe REDO could offer a refinement to request the locals be kept as-is (or name specific ones to keep?)

It's a low-priority feature, but I had to document what it was for Redbol...and I figured having a post to talk about it was better than writing an essay there.

How about adding something like ...

Func[a /depth []][]

This way checking that this refinement is only used in recursive calls from the same function / from within the same object might be possible.

1 Like

func [a /depth []] []

That seems like a pretty good idea... a refinement which advertises no legal types by which you could call it! Nice way to take advantage of the refinements-as-their-own-arguments...

Definitely will keep this idea on the radar.