Analog – An analogous, indented syntax for the Rust programming language
github.com
github.com
What is the goal of this specification? Maybe I missed it in the point. Also the name might be a bit challenging considering the overall footprint and overloaded nature already of the word `analog`.
• Why?
• You’ve made two mostly independent major changes in the syntax: to depend on significant indentation, and to switch from keywords to symbols. Why both, rather than just indentation while keeping it otherwise as familiar as possible?
• Why change lifetimes from ' to `? (Nothing obviously collides with keeping it '.)
• Why change closures from || to //? (You’ve then reused | for other things, but why not the other way round?)
• What does the word “analogous” mean here? (I don’t believe it can be the normal English sense I’m used to because that requires something in the syntax to be compared to—I’m not sure what the proper grammatical term is on the adjective, but it’s like the distinction between intransitive and transitive verbs, with the sense I know being strictly transitive (by analogy) but you using it intransitively (as it were).)
Finally, macros are a knell for alternative syntaxes like this: it’s not possible to make them work interoperably, because macros consume Rust token trees, but can interpret the body however they like, and you can’t reliably detect what they’re trying to do, so you can’t compensate. In your println! example, you are apparently saying it must translate the body from Analog to Rust, which would work in that instance because it’s largely consuming Rust language syntax (though named arguments add a little fun); but in your json! example, it needs to not perform any such translations, because json! doesn’t consume Rust syntax. (Perhaps in that specific instance the translation would be harmless, but it’s trivial to show macros where that’s not the case.) Attribute macros will be OK, and you might be able to get most structural macros working, but many structural macros (especially those that deal with token trees) and all non-attribute procedural macros are going to be a dead loss. I’m afraid I see only three solutions for macros in such an alternative syntaxes: (a) give up on interoperability, and maintain a completely distinct macro system; (b) have macro bodies always be in Rust syntax, which would break your very first example’s use of println!; or (c) on macros where there is any uncertainty, require what basically amounts to FFI bindings, which the language could provide for the standard library but it’d mean pain for calling macros from third-party crates, and could basically entail falling back to Rust syntax in complex cases. Hmm… that’s actually interesting to think about, as the uncertain nature of macros already causes some trouble for developer tooling. (rust-analyzer works well with structural macros, but can’t cope very well with most procedural macros, and something along these lines could actually be genuinely useful.)
To represent all of Rust with only only a small set of ASCII characters, while keeping visual noise to a minimum, and still representing what the code was supposed to do was quite a riddle. I enjoy riddles.
- Changing lifetimes was due to the fact that ` does not conflict with any other part of Rust, I felt the concept of lifetimes deserved its own symbol, and it is much more readable when used as a prefix/suffix on a set of letters.
- The // characters stand out in a group of letters and numbers moreso than ||. In this manner, I can add highlight/significance to closure calls which can be deeply nested in function calls which are mainly chars [a-Z]. Also if you imagine you are in a circle: things loop in a straight line ||, but // would be take you on a diverging path.
- "analogous" "strictly transitive (by analogy) but you using it intransitively (as it were)"
This is very much intentional. Nouns do not exist in the real world, everything is verbing all the time. Languages have failings, they depend on the users of the language to use/redefine it as they see applicable.
The initial goal was to design an 'analogical' language. Or in other words: a language in which it's symbolic representation would correlate to the concepts being implemented. The first commit of the repo exemplifies this more.
- The macros can be translated directly into Rust, the first example is valid. Indentation would be substituted for a { on the macro call and the end of indentation would signify the }. As far as I know, leading and trailing whitespace is not a concern with Rust's macro system.
a) Making its own macro system would be futile considering most expressions haven't changed. It just requires more cognitive overhead to remember the ),; where applicable, but at least you only have to think about that stuff in macro calls.
b) If someone had to fallback to Rust when wanting to use this syntax, then the whole thing would be pointless and I would lament my time spent designing it to not learning how to bake crumb cake.
c) Now that's a lot of syntax trees...
macro_rules! alpha {
(
$(<$($beta:ident),* $(,)?>)?
for $gamma:ty;
$(#[$delta:meta])*
struct $epsilon:ident default $zeta:expr;
## $eta:tt // And I’m not telling you how I’m using this one
$theta:item
) => { … };
}
I can’t see any way to make this work with Analog syntax in such places as $gamma, $zeta and $theta (and who knows about some of the others like $eta?). I’ve made this a fairly extreme example, but simpler examples that are still problematic are widespread.Perhaps I might put it this way instead: can you write out for me what an invocation of that alpha macro (which is defined in Rust code) from Analog might look like?
I now see the issue this raises and have discovered a solution through a macro!! call.
Just guessing, but I bet it's to create a parallelism between // (closure) and \\ (closure that moves). If closures were ||, there wouldn't be quite as much of a same-but-different feeling as you get with slash & backslash.
If anything in the syntax has ambiguity in this regard, please do share, as I am not dyslexic.
(I notice a few comments describing this syntax as less readable. My first thought was that these comments were sarcastic/trolling, but I've concluded that they're serious – but written by programmers who think very differently than I do!)
Had to do a double-take to make sure this wasn't /s
It's my opinion that the use of random symbols is the primary reason math is so inaccessible. It is for me, and anecdotally I've heard dozens of others say the same thing. The only imaginable benefit I can see to using symbols is that they're easier to write quickly on a chalkboard.
By doing this consistently, I can highlight the link between math symbol and corresponding concept, and help the student learn how to read and interpret dense constructions as we go.
(Note that this approach is intended to be used as part of a larger explanatory strategy, so please assume the example is embedded in an appropriate context.)
Is this coded? Do you have it open source?
"Readability" is a highly, highly subjective matter, and depends on one's education, background, knowledge, experience etc. People seem to use it as a shorthand for "math-like shorthand", but that's not what I mean when I say readable, it's like we speak in a different language.
Personally I find the adjective "readable" a major trigger, it seems to be thrown around by people who are either trolling or who seem to live in a kind of a social (online) bubble and don't know anyone else with a even a slightly different background. This is always slightly shocking to me.
But just bad as or maybe worse are the variable names which are random, short, and completely non-descriptive. If you compare it to source-code, it's like the worst code ever written.
Words may not be the optimal format to express equations, but are the lowest denominator that would allow everyone to understand if they knew the concepts.
That should illuminate why your opinion and other people's opinion of this differ. You likely each have different relative opinions and skills reading syntaxes like this.
I haven't and it continues to be a massive source of shame. Maybe today's the day I do something about it.
It's not a random event that digits - essentially symbols for numbers - were invented millennia ago.
C-like syntaxes are more popular than lisp syntax(for example) not because it's inherently better but because most people have some exposure to it at this point through other languages they have been exposed to. Interestingly, that actually does make it inherently better in some aspects when choosing a syntax to adopt, such as learnability. For the person that only knows C and the person that only knows Lisp, which might have an easier time with the average rust program? What about someone that only knows Prolog?
The issue is not about only using words, the issue is about using things your audience already knows. People use numbers in letters not just because words are less convenient, but because words are less convenient and they know the people on the other end know Arabic numerals so that's a valid medium to rely on.
For programming languages it's less that words are better and more that there are either different or conflicting meanings for many symbols leading to confusion for those that deal with multiple languages or are new to the current language, so time needs to be taken to internalize what they mean in this new context. That doesn't make them bad or worse than words, but often they necessarily are less obvious in meaning to those new to the language to benefit those that are more familiar or experts.
Don't worry, I assumed the exact same about your comment. I still have to remind myself sometimes that people use Python because they think it's easier to reason about.
Take these three lines from the README example:
|?| ^door <= 100
^door_open[door - 1] !^door_open[door - 1]
^door += pass;
Without the docs I don't have any idea what this does. Even with the docs it makes very little sense to me. |?| seems to start a loop, but it doesn't seem to be in the documentation (the closest thing is |??| for "while"). ^ means mutability, but is needed in the first line for some reason even though no mutation is happening? (Or is mutation happening? <= doesn't seem to have changed meaning though.) But then ^ is not needed on the mutable "door" in the second line? What does the second line do? It's not assignment, according to the docs, because that is a very un-mathematical prefix thing (but everything else, including other assignments like +=, is infix?). It seems to be just two expressions next to each other, and the docs don't seem to say that's meaningful. Why does the third line have a ;, but not the second? Is the second line somehow part of a statement finished on the third (which isn't indented)? Is the second line a ?-less condition affecting the third?I am not being sarcastic or trolling. I genuinely don't understand this. The assumptions I'm aware of making are (1) the documentation covers everything new in the new syntax, and (2) the example in the README is valid code in the new syntax.
Serious question: do you understand these "much more readable" three lines?
I think you have to take into account familiarity here. Rust uses "fn" for functions, which is really short (the same as := really), but it's easy to see how it's the short version of "function". On the other hand, := is assignment in some languages (usually Pascal descendants), and is used for "definition" in BDNF. That makes it hard to map it to "function".
While I like `^` for marking mutability, other things might not need to be so abstract (`=:` instead of `fn`) or re-purposed, like using `|i|` for loops instead of a Rust closure.
Heh. As a self-taught developer with a dumb ape brain, if anything I wish it was more consistently verbose. I've read through the Rust book and tutorials multiple times, and I've still not been able to wrap my brain around how lifetimes work. Personally I think the incomprehensible <a'> syntax has a lot to do with that. I'd love to see a kind of pseudocode that compiles to rust simply for the purpose of making it easier to learn the real language.
Maybe you're thinking "wait, lifetimes are easy, you just..." but I think the reality is probably that most people who want to learn programming on their own, don't have a job in the field, and have never had a compsci class are going to have trouble wrapping their heads around the concepts. I can write good Python code. I can write safe C code (because it's easy to understand explicit allocation and freeing of memory), but I've never been able to make lifetimes work in my head. The complexity of rustc's error messages doesn't help much either, last time I tried it.
Lifetime in Rust is actually an explicit generic type system over lifetime is a new concept (you would not see it anywhere else) so it will indeed feel alien as you can't map it to other construct that you have already learned in other language.
So to learn it you must treat it as such: a new concept, learn it by remember how you first learned recursive (yes, Rust's lifetime is not harder than recursive), pointers, ... Don't expect it to be easy or familiar because you already know how to code in other languages. I see many people struggle with learning Rust just because of this weird expectation that new language should be *very* similar and they do not have to spend much effort to understand.
The issue isn't so much that you can't map lifetimes onto a concept from another language so much as it is that you can't map the concept onto anything you already know at all. If you know what a function is (i.e. you've taken an algebra class required of all U.S. students), you can work out for yourself in 10 minutes how a recursive function works. It may be surprising at first that a recursive function can return a value (instead of just calling itself forever), but by examining a simple one (like nth Fibonacci) any student with an understanding of algebra can determine that it does.
Incidentally, most high school students who progress as far as calculus meet with recursive functions like the following (though usually not expressed with this notation):
f(x) = x/2 + f(x/2)
I'd say the concept of a function is itself something new and extremely difficult (I remember finding the syntactic ambiguity between function notation and multiplication very confusing), but fortunately most students encounter it well before they ever try programming.What you refer to called lifetime elision, and there's an exact rule set [1] for that, so you will know exactly when lifetime are required, but you can always explicitly specify lifetime for every reference. About the why: lifetime is a tool for you to give a constraint to your function regarding lifetime of reference (and reference-based struct) of your input and output. My favorite example for introducing lifetime is:
fn take_until<'a>(source: &'a str, token: &'_ str) -> &'a str {
// take str from source until hit token
}
means that the output can not live longer than the first input, and it doesn't have to care about the lifetime of the second input.My mention regarding recursive is more about programming and related concepts (terminal conditions, tail recursion, avoid infinite recursion...) than math recursive. For math, I was so proud of myself when I first understood and solved a recursive induction proof.
[1] https://doc.rust-lang.org/nomicon/lifetime-elision.html#life...
Reserved words may be inelegant in principle (since they intrude on the namespace for identifiers), but at least they're easier to type on most keyboards. And Rust seems to be going for a rather minimalistic syntax already. It's certainly not as verbose as languages in the BASIC, Pascal or Ada lineage, let alone Cobol.
The semantic meaning of the 32 ASCII non-whitespace punctuation characters are so tragically overloaded between languages (with subtle, major, or even contradicting meanings).
The difference in meanings of ASCII characters usually varies within a language or requires “digraphs” like := or “trigraphs” like <=> and I laugh at our ancestors* using 6-bit* trigraphs (then I feel shamed that we have only progressed to 7 bit trigraphs).
* poetic facts + punctuation.
PPS: Any recommendations for a reliable swype keyboard for iPad that also accesses common Unicode characters such as dagger? I find Gboard on Android to be far superior to the Apple virtual keyboard.
Also, OP, you cannot post your own stuff to HN without "Show HN:" at the beginning of your title.
> Off topic: blog posts, sign-up pages, newsletters, lists, and other reading material. Those can't be tried out, so can't be Show HNs. Make a regular submission instead.
https://news.ycombinator.com/newsfaq.html
Judging by your first sentence it is correct for it to not be a show hn.
Scope is a lie anyway, right!?
I'm glad to see they didn't start implementing anything.
"Don't be snarky."
So far I believe every such attempt has ultimately failed. Like CoffeeScript.
This seems to be a particularly execrable example of that phenomenon.