De-Verbification: NOUN OF X vs. NOUN X ?

Recently I made the same mistake several times, naming variables things like first, last, head or tail when those are verbs in historical Rebol. One case was porting some unrelated C code which named a variable "first". The other case was just repeating some habits from inside the interpreter implementation, where you can freely call things "head" or "tail".

This led me to wonder how much value there is in saying head x vs. head of x. It's pretty easy to type the latter, and the flow is reasonably natural.

Here's a line of historical code from CLEAN-PATH, which is a "heavy use case" of those functions:

if all [#"/" = last out #"/" <> last file] [
    remove back tail out
]

Today we can make that flow more nicely:

all [#/ = last out, #/ <> last file] then [
    remove back tail out
]

Now imagine that we rigged it up so that you had to use OF. I think even in this heavy usage case it's not so bad...arguably clearer:

all [#/ = last of out, #/ <> last of file] then [
    remove back of tail of out
]

I think it's reasonable to say that if you're working on series a lot somewhere, you might find it convenient to shorten the notation...but as POINTFREE gets more convenient, maybe you could set those kinds of shorthands up as:

last: (<- last of)
tail: (<- tail of)
back: (<- back of)

The key here is asking if these shorthands are really the ones you want; or might it be more empowering to leave more words free in global space (especially ones that are more obviously nouns).

I've mentioned that it's becoming second nature these days to write things like first: first of x without blinking. It feels like we'd be better off going this route.

There's some technical questions here, about expanding the "OF-context". Things like FIRST and SECOND are specializations set up in usermode of PICK. So how do you put these specializations in where OF can find them? This suggests OF is picking out of an extensible scope of property accessors. Maybe if things work out with dualism of actions and objects, the context could have the same name...so you could just say:

>> of.first: <- pick _ 1

>> first of [a b c]
== a

Pushing on such approaches seems crucial to the problem of modules and contention for short words...

I like the brevity of the current behaviour.
For example
head of [a b c]
means to me: a
head [a b c] means put the series index at the beginning.
Some things are just the way they are in Rebol, and yes we all suffer from the occasional oversight of words that are already taken and now overwritten by the new definition. Especially when converting stuff from other examples and languages this can happen (a lot) but that could hold true for a translation toward many other languages from such a source as well.
When you aim to do (more) automatic conversions (to keep up with upgrades in the original source) you might be inclined to smoothen this process, but a variable name like 'first' could also get renamed by the original devs to 'start' and last could become 'exit' (and for langs that use exit think 'quit').
What it comes down to is you have freed up this gem, for the sole purpose to please OTHER languages.

And another point one can make is, first is a bad name, first what?

I'd need to think about this. I quite like OF in these simple examples, but when I read at code like this (in a script from from Bohdan Lechnowsky), and think about adding OFs, I'm not so sure:

days: head remove back tail insert head copy system/locale/days last system/locale/days

I would never write something that looks like this snippet, but I can understand what might lead someone to write this.

1 Like

Jun-2022: Post moved here from the thread remarking on Kaj's Meta Project

I looked to see if there were any updates on Kaj's Meta project, and it looks like he is kind of focusing on porting existing Atari 2600 demos written in assembly to the language.

There the usual debate on the Atari forum of people talking about why languages succeed or not, asking and trying to get at why anyone would care about Rebol, expressing skepticism that Kaj will strike gold...but he says it's successful even if he just uses it and it gives him a "competitive edge".

If you're interested you can read the threads:

https://atariage.com/forums/topic/326694-meta-language-released/

One thing I noticed is that he has chosen to go with FIRST-OF instead of just FIRST. I've questioned this before, because things like NEXT and FIRST are useful variable names. In Ren-C the hyphen is optional, so you could write either:

first: first-of block

first: first of block

Similar to how you can today say:

type: type of block

This is because OF quotes its left argument, and dispatches to whatever is named XXX-OF, with the idea that this is learnable.

4 Likes

I think I'm okay saying that people who do this kind of thing can pick a helpers lib that specializes FIRST and LAST and such.

But in the core, these make more sense to leave undefined.

The only thing really blocking me at this point is that I don't have an answer to how these things that extend OF get registered. But this is another epicycle of the many unanswered things about function/method dispatch in the language.

I feel like hacking it in wouldn't put us in that much worse of a position than we are already... and it would free up these terms for variables in the common case.

I'm no longer blocked!

I now believe that things like FIRST and NEXT should be left open as variable names, so you can write things like first: first of block or next: next of group.

And it's easy to alias them, just say first: first-of/ and you'll be set, if you're working on a file or in a function where you're doing a lot of series manipulations and it's worth sacrificing the words.

Only Question Now... Is BACK the Right Term?

back of series sounds a bit too much like what TAIL does. e.g. BACK is the opposite of FRONT, not of NEXT.

Rebol 1.0 used PAST, which is the opposite of FUTURE, and I don't like it.

How about PRIOR? It's shorter than PREVIOUS, just one more character than BACK.

pos: prior of series

take prior of series
1 Like

I think things like second of block are a pretty literate choice, and should be what you go for first.

>> second: second of [a b c] 
== b

However... for those who like brevity and don't want the trouble of aliasing the words, we can provide PICK(N) variants.

>> second: pick2 [a b c]
== b

That's a different meaning of 2 than the 2 in COMPOSE2, e.g. pick specialized with 2 vs. an arity-2 pick. :person_shrugging:

I never made the change to first of block / first-of block, because outside of the example I ran into from porting C code... I kind of felt skeptical that variables named FIRST or SECOND were all that common. And while wanting the second or third item in a block isn't common at all (at least in my code), wanting the first item is very common.

Iterators have brought the necessity for an operator that gets the item that you are "AT", and while we could say that operation was FIRST, I think it's better to make AT an arity-1 operation. And with the idea of iterators that I'm promoting, I believe that should work on series as well.

So it seems to me with AT on the table to get the first item in a series, it's no problem to say FIRST OF or FIRST-OF ... given that you're being verbose for the sake of verbosity anyway (you could have said e.g. block.1)

But the idea of fusing iterators and series came with another thought: if you're going to update an iterator in-place to its next location without cloning the entirety of its iterator, what do you call that?

You might say "oh, you could call that ADVANCE" and it would be different from NEXT. So NEXT would make a copy of the iterator, and advance it.

That sucks a bit, because it means ADVANCE can't work on a series like BLOCK!... the position in a block is "immediate", it lives in the cell. So if you do block: [a b c] and advance block there's no way that ADVANCE can change the value held by the block variable.

This gave me the idea of letting you pass the variable in to operations like NEXT to modify them:

>> block: [a b c]

>> next block  ; passed by value
== [b c]

>> block
== [a b c]  ; variable unchanged

>> next $block  ; passed by variable
== [b c]

>> block
== [b c]  ; variable updated

That's an interesting idea, but OF doesn't sound like it changes things:

>> next of $block  ; hmmm?
== [c]

>> block
>> [c]

There's no technical rule against this, at least not yet. For instance, OF is not defined as "PURE" (and it actually currently can't be, because NEXT-OF commutes the mutability of its input, and any function that does this has to be "agnostic" instead of pure).

But it's weird to have XXX-OF be something that mutates. And if you're working with iterators and have an operator like at iter, it seems like iter: next iter or next $iter are cleaner.

And Then... There's PARSE

I've suggested that series options that make it into PARSE basically just drop one argument: the parse input is implicitly the series.

This makes SKIP and AT and NEXT fairly obvious names for parse combinators that fit that.

So I feel like NEXT and BACK wind up being better as verbs, here.

What About HEAD and TAIL?

These are the only two I changed so far. They face some of the same problem if we wanted to use them as iterator operations that didn't create a copy of the iterator to seek its beginning: head $iter as an optimized version of iter: head $iter. That's weirder with head of $iter.

Maybe I've Been Thinking About It Wrong?

I have this MY operator, which is supposed to be fully generic... but maybe... it has optimizations?

What if when you wrote:

iterator: my next

It didn't turn that into iterator: next iterator by rote, but conceptually (if not implementation-wise) became something more like:

my-next $iterator

:thinking:

That opens the door to the idea that MY-NEXT can be implemented on top of iterator advancement, or a series variable can be updated, etc.

This seems promising, compared with the idea of operations like next $iter doing something that doesn't look like an assignment.

So a smarter MY...something that's not just a simple macro...seems like it can probably finesse making blind copies of iterators when you intend to update them. All with a nice syntax!

some-obj.some-iterator: my next  ; this can be efficient!

What About "Advancing AT", then?

If MY is the device by which you can mutate things like variables in blocks while also doing iterator options in-place, that doesn't address my "AT and ADVANCE" idea:

>> block: [a b c]

>> at block
== a

>> block
== [a b c]

>> at $block
== a

>> block
== [b c]

It's a weird shorthand for at block, block: my next, which I think happens a lot. But it does obscure the reassignment, breaking from the "good" pattern set by the smarter MY.

Maybe that just needs another name. I've called it GRAB in other places.

It would be nice if GRAB could be generic, but following the same pattern as MY would be misleading because the result of the assignment isn't what you want:

 >> block: [a b c d]

 >> block: grab at
 == a

 >> block
 == [b c d]  ; weird because result of assignment said a

 >> block: grab two
 == |~[b c]~|  ; antiform (splice!)

 >> block
 == [d]  ; weird because result of assignment said [d]

GRAB could be based on multi-return :face_with_diagonal_mouth: and you could "circle" the result:

 >> block: [a b c d]

 >> [{} block]: grab at
 == a

e.g. GRAB would look for a SET-BLOCK literally on the left, use the second item as the thing to grab, and decay to the first item. But this is all just a weird pattern for obfuscating that what's really happening is stuff like:

grab $block at

grab $block two

I think this is better done with higher level ideas, like "parse pumps" (or just iterators).