Skip to main content

Syntactic Tweaks

Jake Russo

Jake Russo

Quarrel Designer

It's been some time since Quarrel has called me back to it. I wish I had the foresight to capture what insightful thing returned my mind's eye to gaze upon it's many jagged edges. I did return to reading about the Syndicate concurrency model but I don't recall what led me back to that material. Inspiration aside, I've been able to add some additional content which I will heretofor describe unto thee.

Though not my initial driver, I had a smaller eureka moment in the car this afternoon. I realized that I needed a syntax for constants and that some of the programming language's usefulness is what it tells to the compiler to help write faster, safer, hygienic code. The constants of many languages get some additional performance characteristics due to their being "finalized" or "set in stone". Quarrel has a unique copy/reference assignment dichotomy and I don't think constantness or immutability has a place in the assignment operation. It's more about changing the characteristics of the container (assignee) of the operation. I realized that I still could utilize the $ in a more prevalent manner and how it generally fits due to the syntactic familiarity provided from languages like Perl or PHP. That being said, here, it provides a very specific meaning of constantness. It is my hope that I could even model these as more of a raw "unboxed" container which perhaps may provide a beneficial optimization down-the-line. I think this syntax looks clean and feel comfortable committing it here instead of holding it for any further complex language features. I also find it striking that it shares a similar "locking" action as the Excel cell reference operator.

During this process, I also had a skeptical/concerned moment thinking about the "null" or "empty" language feature as (). Because that empty sequence is like a lazily evaluated thing, you won't know what it contains until you invoke it. On some intuitive level, I feel that this cannot jive. It's always felt like more of a "this replaces that with some arbitrary thing that means 'empty'" instead of feeling like "oh yeah, that definitely means 'empty'".

Since sequences/routines can't actually avoid returning something anyways (the last expression is always returned), it would be expressly difficult to avoid sending something back to the invoking scope. So it seems fitting to slightly tweak the "empty" operator by just invoking that empty sequence: ()!. This feels much more intuitive: the empty sequence is being invoked which returns emptiness. You can supply it to a container to indicate that this container is empty. Checking ()! == ()! returns yes but ()! === ()! returns no. This is because the compiler knows that they are always equivalent to each other but they are actually two separate results of two sequence invocations so they are not identical.

After that, I focused in on fleshing out transforms and then even set my sight on the already-somewhat-filled-out conditionals section. Starting where the page starts, conditionals got a nice refresh in some descriptions and a cleanup of the bullet list to track with the ()! change. Also, a clearer explanation of the right hand side and a warning about the ambiguity pitfalls that are present when you take out delimiting circumfix operators.

Finally, two new sections were added on transform operations. Since these operations are dependent on what operands are provided, each section will be more about the unique combinations and how they act. I may refactor this section or at least do some renaming to help it be more succinct and precise.

Lastly (truly now...), I have been toying with a rather fundamental change to aggregate invocations. I feel that generally, I am wasting a few operators to make a uniform system: [] literal circumfix to the a[] postcirumfix method operators and <> literal circumfix to the a<> postcircumfix method operators. I have always felt that the one that is especially confusing is (). The literal syntax is a series of expressions that are procedurally executed. The postcircumfix version is a positional list of expressions that are not executed. It's basically a list. This feels odd and generally, whether you are looking up a list index or a map key or applying a sequence, you are doing the same thing: invoking. Perhaps it would make better sense to lean on the [] postcircumfix since that one at least feels proper due to it needing to be a list of arguments. This frees up <>, {} and () for other postcirumfix forms. Like more of a "tagged literal" sort of thing, and then foo[] is just one form that takes the literal argument list and supplies it to the receiver container in an invocation (whether it be sequence/routine, list, or map). With this, you lose some minor typing info from the three-fold postcircumfix form of yore. The map is still supported by the unique \ shorthand which is where it really needed some syntactic terseness. Also, the {} tagged literal is already utilized as a shorthand way to name a pattern (which allows for nested patterns, a very useful feature for more complex structural data).

I haven't quite thought through what the newly freed <> and () postcircumfix operators should do. I also need to think about the method operator forms that I had developed to house much of the builtin standard library type functionalities. Will these still "fit"? On top of that, this kind of change will require a full scrub to ensure every outdated use is patched.

It was a good amount of work and I am glad to see I could clone and start working in this project without any hassle (Docusaurus rocks!). Next time, more transform details and some more work on that syntax change described above. Thanks for reading!