FUNCTION-MU: Rebmu's Function Generator Dialect

Rebmu is a laboratory for cool dialecting tricks... one of those tricks I imagined was to see just how much mileage we could get for defining functions with a single character primitive that has no holds barred...

F

The idea is to make F use tricks with quoting and variadics in order to eliminate the need for certain boilerplates. For example, it can quote its first argument, allowing a WORD! without a block around it to use as a spec for an arity-1 function:

f x [...]  ;  Would act the same as f [x] [...]

Bear in mind that in when "mushed" in Rebmu, that would be fX[...] acting as f x [...] because the case transition signals when a new token begins.

And a quick reminder of the game for anyone who has forgotten: if a run of transitions starts with a capital letter instead of a lowercase one, the first acts as a SET-WORD!. So Fx[...] would act as f: x [...]

Being able to avoid the brackets of a spec block and quoting a WORD! is trick number one. But that's old hat, and even lambda uses that. x -> [...]

So let's get crazier. :crazy_face:

Automatic Naming Of Multiple Arguments

The first generation of Rebmu introduced things like B-FUNCTIONS or C-FUNCTIONS. The concept was that if you asked for such a function, you'd actually get arguments for a series of letters.

 fc [...]  ; acts like `f [a b c] [...]`
 fd [...]  ; acts like `f [a b c d] [...]

This idea for taking letters at the start of the alphabet was complemented by functions that counted letters backwards. This way when you were declaring functions inside functions, the names would be less likely to collide:

 fx [...]  ; acts like `f [z y x] [...]`
 fw [...]  ; acts like `f [z y x w] [...]`

Sacrificing two letter names starting with F for this is probably too much, so you might imagine this coming from ISSUE! usage:

 f #c [...]  ; giving `f [a b c] [...]`

But at best that would mush as f#c[...], at which point you're using up two characters. So if the word-named feature were variadic you'd break even on two characters:

 f a b [...]  ; if this meant `f [a b] [...]` you could write as `fAb[...]`
 f m x [...]  ; and you could pick any characters you wanted, `fMx[...]`

So the idea would only make sense if there was another meaning for things like fMx, which leads us to...

Removing The Need For Blocks In The Body

If we look aside from the spec for a moment, we can ask how quoting might be used in the body. For instance, what if instead of writing:

 f [args] [case [...]]

You could just write:

 f [args] case [...]

That points to an alternate potential meaning for something like fMx[...] as f [m] [x [...]] instead of f [m x] [...], and is probably a more potent idea.

This behavior when the body isn't a block would essentially flip F into a mode of being a spin on POINTFREE...which would try to consume "one whole expression's worth" of body code based on what it could figure out from the material it variadically quoted.

Such a feature assumes that generated bodies are the unusual case you would use GROUP!s to escape. So while in plain Ren-C it would run the reverse once on function creation and make a function that prints:

 func [] reverse ["Hello" print]  ; e.g. `func [] [print "Hello"]`

We'd see a different effect, making a function that returned a BLOCK!, calling reverse each time:

 f [] reverse ["Hello" print]  ; e.g. `func [] [reverse ["Hello" print]]`

You'd thus need some kind of escaping mechanism like a GROUP! to get the "use code to generate a body" behavior:

 f [] (reverse ["Hello" print])

I think it probably makes sense to have the generator that gets the single letter NOT be tailored to generating body blocks from code. The F would not be an alias for FUNCTION (obviously) but for FUNCTION-MU. So FN would be just one letter away for anyone who wanted the more conventional version.

The LET and Auto-Gathering Distinction for Locals

When you make an assignment to a SET-WORD! inside the body of a function to something that isn't a parameter, how is this interpreted? How do you designate a local variable vs. reaching outside into the enclosing binding environment?

We still don't have answers to this in the language proper. Which is frustrating. :frowning:

But if there's anywhere that auto-gathering of SET-WORD!s as locals is useful as a feature, it's in Rebmu. So:

  • Is it the default behavior for F?
  • How do you turn it on or off?

I'll point out here that how characters are counted in code golf varies...but if all of unicode is allowed where unicode characters "cost" the same as ASCII, there are choices like dotted-F () on the table. Some golfing languages go crazy with this. But as a general baseline, I think ASCII should be used and then have Unicode definitions as an extension.)

For now, I think the default should be to assume arguments are not local. It may be that defining locals is done with a separator like BAR!:

 f[aB|cDe][...]  ; acts as `f [a b <local> c d e] [...]`

[Your Brainstorm Here]

Feel free to throw out ideas for any combination.

For instance, what if the body of a function is a text:

 foo: f [] "Some String"

It doesn't make a lot of sense for that to mean "evaluate to the string", because you could have just assigned the string to the variable directly:

 foo: "Some String"  ; shorter for same effect

This would suggest that maybe the body being a string means it should print that string. But if that's all it does, then why would it need a spec? So if F gets a string in the spec position it could consider that the end of the variadic definition, and make a function that prints that string.

 >> h: f "Hello World"

 >> h
 Hello World

ISSUE! can print strings without spaces with one less delimiter, so there's that. But I kind of liked my idea above about auto-generating multiple parameter names. Then again, there's FILE! for an alternative: f %c [...]

So what if the spec is a TUPLE!, or a PATH!, or if the body is a SET-GROUP!?? What kinds of practical shortcuts can these take to get the maximum leverage out of F? Maybe if an INTEGER! is in the body position it assumes that means to loop that many times.

 >> x: f y 3 [p y]   ; let's say P is PRINT
 >> x "test"
 test
 test
 test

The concept of "pulling out the block" mentioned above could even cascade there, letting you write x: f y 3 p y which can mush as XfY 3 pY

There's a lot to explore here, and @razetime please let me know if this is making any sense or not....I've started on bringing Rebmu back to life and hopefully will have it in usable shape this week.

2 Likes

I was just reminded of a concept that needs to be integrated to F somewhere... a variadic function that TAKEs variadic arguments each time a variable is referenced.

vfunc: func [
    return: [action!]  ; generates a function from the passed in body
    body [block!]
][
    ; generated function has variadic argument `arg`
    ; also has local `v`, set to a function that will take the next
    ; variadic argument.
    ;
    return func [arg [<variadic> <opt> any-value!] <local> v] compose [
        v: does [take arg]
        ((body))  ; compose contents of body block into generated function
    ]   
]

So just mentioning the variable will get a new value variadically from the callsite each time:

test: vfunc [
    loop 3 [print [v]]
]

>> test "a" "b" "c" "d" "e"
a
b
c
== "e"   ; d and e were skipped, overall evaluation was to "e"

If you have a case where you only use each parameter you take one time, then this not only saves on the character space for naming the variables...but also avoids overwriting names that might be used for other purposes.

This might make an interesting behavior for something like f %v [...], which can mush as f%v[...] It would also be possible to use the name of a quoted word variable to cue the behavior, e.g. say that if you do f v [...] it might trigger a behavior that you wouldn't get with f a [...]. While I don't want to rule out such lines of thinking completely, I'd like to be careful about doing that kind of thing if it's not absolutely needed.

I'm pretty sure I had a more clever nuance to add to this trick, which involved the variadic modifying itself somehow to become the value instead of being the function. But I can't remember quite how it worked. Something in the spirit of saying the function was arity 3, so that when it reached the third time it was referenced it would replace itself with the value and subsequently always give that value instead of doing another variadic take. It was similar to that, but not that. Maybe I'll remember it.

1 Like

I've been a bit busy with a lot of college work lately, and brushed up with some tutorials today. Some of this makes sense to me, and most of it does not. I'll be replying to this thread with a lot more questions when I start to figure things out(and solve simple challenges in RebMU). Sorry for the late reply, and thanks for writing all this up.

1 Like

Do take your time! :slight_smile: I've wound up sidetracked (very predictably) hacking on things that are prerequiste to defining Rebmu features concisely.

So much hinges on the design of FRAME!. Frames give you usermode control of function calls just as if they were objects, and there's a duality between turning a function into a frame and a frame into a function. It even works for RETURN.

 foo: func [f [frame!]] [
     print "Entering foo"
     do f
     print "Exiting foo"
 ]

 bar: func [] [
     print "Entering bar"
     f5: make frame! :return
     f5/value: 5
     foo f5
     print "Exiting bar"
 ]

So if you run this, you get:

>> bar
Entering bar
Entering foo
== 5

The DO of that FRAME! managed to dispatch the bound return... specialized with 5. It knew which function to return from (BAR) because each FUNC makes its own RETURN action that it holds in a local variable named "RETURN".

But it's a duality. Once you have the frame, you can make a function out of it. So r5: make action! f5 would give you a function you could call without the DO. If your frame didn't have all the values, it would be partially specialized.

Anyway...this is all stuff you won't find in Rebol2 or Red. But among the many design revolutions that make Ren-C more powerful for anything, including Rebmu.

1 Like

This is the Rebol universe. Used to late replies. Soon (tm) has its tm for a reason! Anyway some college stuff has prio over this at any time. But remember college is prio now, Rebol is forever.

1 Like