What to Do About FUNCT (including "Not Calling It That")

FUNCT was an R3-Alpha-ism that made it easier to keep track of one's declaration of local variables in functions. It walked the body of the function looking for any SET-WORD! usages. If those set-words didn't name arguments or explicit locals...and weren't explicitly specified as being non-local via the /EXTERN refinement...they would be automatically made local.

It gained popularity since declaring locals manually is more of a hassle--and stale locals are hard to notice and can linger after their references are all removed. Since it was popular, several people (myself included) lobbied for it to get the name FUNCTION.

However:

I feel pretty good about the current plan to switch over to a mix of virtual-binding-based LET...with explicit <local> when it makes sense. This means FUNCTION as we have known it (which is to say FUNCT) is going to die.

Plan is to Temporarily Deprecate FUNCTION in favor of FUNC

I'm going to deprecate FUNCTION for a while... and just use FUNC with LET (or <local>). Once this has gotten wide acceptance, we can reintroduce FUNCTION as a synonym for FUNC.

This is a slow process, because doing the change across large bodies of code is exposing issues in the virtual binding mechanics. So I don't know the exact time when FUNCTION will be going away.

But I have committed one step of moving away from the "gathering of SET-WORD!s" model. I changed METHOD to no longer be the "gathering form of METH". This gave me an idea of how hard it would be to get a bootstrap executable to fake up LET mechanics...even though it doesn't have virtual binding.

I left behind METH/GATHER as a compatibility form... just because that's how it was implemented prior (method: :meth/gather).

Ideally you would not use METH/GATHER, instead you would convert your code to use LET... unless you think your code is too performance sensitive, in which case you would declare your <local> variables in the function spec.

What's the Role of the /GATHER Mechanic, And What to Call It?

I think locals gathering through SET-WORD! is a good code golf tool. But does it have a place in the system besides that?

I'm not thrilled about leaving it on FUNC itself as part of the core "important" code... as a refinement or as part of the spec language.

Does anyone have a super strong vote for it, or it needing a name, or does LET + <local> feel like it covers enough for what's needed in the box?

Naming local variables in the function spec is the worst form of locals declaration, except for all the others.

It's an awkward dance, but I don't know of any of the mainstream languages that don't have some awkwardness about them here. Obviously I'm not against locals gathering in principle or I'd never have gotten past using make object! so I can't quite put my finger on why I still tend to use func and eschew locals gathering for functions.


Mulling it a little further, I'd say one aspect—which sort of held until recently, but that contract has changed—is that I want my spec to match the internal spec format. That is, I used to be able to say:

spec: [foo /local bar]
body: [bar: foo + 1]
thing: func :spec :body

equal? :spec spec-of :thing
equal? :body body-of :thing

I understand there's a lot of subtleties going on with functions on Ren-C, but I miss that certainty as a start point.


Not a solution, but I'm half tempted to write a gatherer for my text editor that will do auto-gathering in the current block and quantify the local words. As much as an exercise to see if such a thing could be useful.


Before you'd posted this, I didn't realize that function did a deep gathering of words (not being a consumer of said function), I'd assumed it was a shallow gathering similar to make object!—I definitely think deep gathering is bad (and certainly proposes huge problems within modules which are likely not resolvable).


I can't say that—at least that I can foresee—I'd use let over use. use is heavy handed and perhaps not entirely comparable, but is up-front and clear about its business.

1 Like

Red incorporated it for their FUNCTION as well:

red>> f: function [] [do [x: 10]]
== func [/local x][do [x: 10]]

I advocated for FUNCTION taking this behavior when I knew less. Having seen the damage, I think it's a dead-end idea and we need to drop it. So I'm glad you agree it is bad.

However, I still think the following aspects are important:

  • Being able to spontaneously make local variables without communicating with FUNC is important (e.g. something like USE needs to exist)

  • Being more efficient than historical USE is important (R3-Alpha does a MAKE OBJECT! and then has to deep copy and rebind the block to the new variables).

  • Not needing a new level of indentation just to introduce a variable is important.

My goal is to make LET meet these needs.

If you look at the TLS emit example, you'd not be able to use it as a variable declaration surrogate like:

emit ctx [
  TLSPlaintext:
    #{16}
    min-ver-bytes
  fragment-length:
    #{00 00}
]
...
change fragment-length enbin [be + 2] (length of Handshake)
...

You'd have to take a separate block of code as a parameter:

emit ctx [
  TLSPlaintext:
    #{16}
    min-ver-bytes
  fragment-length:
    #{00 00}
] [
    ...
    change fragment-length enbin [be + 2] (length of Handshake)
    ...
]

I feel like variable declaration is something you might commonly want to abstract, and tying such an abstraction to "thou shalt make a new block" doesn't play to the master-of-disguise concept of the language.

Not that you can't choose USE or its ilk. But I think you'd be in the minority for preferring it.

But the devil's in the details...making LET-like functions work is an exploratory and experimental thing that I am only starting to get my head around. I think there's hope for it--although it may force us to rethink the garbage collection method (which needed rethinking anyway).

2 Likes

To be clear, I don't know that I'd agree that shallow gathering in function (as-had-been-presumed) is necessarily bad. I still don't think that I'd use it, but I don't see it as the worst option. I agree that deep gathering is bad for any of the reasons you've described.

If the FUNCTION that does the gathering of the locals gets another name, FUNC or FUNCT are indeed not the best names.

How about PERFORM?