20 ms·
Syntax Design (2014)
- thrtythreeforty 4y agoI find the section on "syntactic salt" interesting: > The opposite of syntactic sugar, a feature designed to make it harder to write bad code. Specifically, syntactic salt is a hoop the programmer must jump through just to prove that he knows what’s going on, rather than to express a program action. This is perhaps an uncharitable way to describe it, but the concept does ring a bell. Rust's unsafe {}, C++'s reinterpret_cast<>(), etc - all slightly verbose. More important than jumping through hoops, the verbosity helps when reading code to know that something out of the ordinary is going on.
- DonHopkins 4y agoAnd then there's Perl's "syntactic syrup of ipecac". https://en.wikipedia.org/wiki/Syrup_of_ipecac https://en.wikipedia.org/wiki/Syrup_of_ipecac
- zppln 4y ago> syntactic salt I feel like this describes Rust's lifetime annotations pretty well too.
- epage 4y agoNot just lifetimes but types used to do more complex lifetimes that are normalized by other languages like RefCell, Arc, etc.
- Karliss 4y agoI disagree about lifetime annotations being syntax salt. At least with my interpretation of what syntax salt. From syntax perspective lifetime annotations are almost as short as possible assuming you want to explicitly convey this information at all ('a is just two symbols and one of them is identifier). The alternative of not specifying it at all comes with major tradeoffs of either in memory safety (like in C) or runtime performance (like most programming languages with dynamic memory management). In theory there is third option of compiler fully deducing lifetimes, but that's far from trivial, has it's own costs and realistically even further narrow down what programs the compiler considers valid and increasing compilation time. There are strong similarities with typing strategies. Just because there are programming languages with dynamic typing doesn't mean that explicit static typing is salt. Dynamic typing has performance cost, and static inferred typing has worse self documentation properties and slightly bigger compilation time cost since you can't process each function independently. On the other hand reinterpret_cast<Foo*>(expr) doesn't provide to compiler any extra information that couldn't be conveyed with simpler less verbose syntax like R(Foo*)expr or (expr as Foo*). Same with unsafe{} blocks, compiler already know which operations are unsafe.
- nyanpasu64 4y agoWhat I can't stand about Rust is that the language developers think they know better than language users developing software. They stack on syntactic salt to make it more unpleasant to write the equivalent of correct C++ programs with aliased mutation or manual freeing, in situations where the idiomatic Rust ways are also unpleasant to write (Cell, RefCell, raw pointers), have runtime overhead (RefCell), are busywork to implement in working programs (restructuring your entire program around an ownership tree with only ephemeral stack-allocated cross-linking &mut, which is only sometimes possible without reducing performance or increasing memory use), or are easy, tempting, and undefined behavior in Rust but not C++ (casting *mut to &mut in an unsafe block in a safe function).
- athrowaway3z 4y ago"Man tries to hammer in screw. Angry at hammer manufactures for not being screwdriver manufactures" ---- I'll just edit this comment to clarify: Rust does a lot of thing really well. Non-RC manual memory management for a tree/graph structure is absolutely not it. As for the verbose 'unsafe' pointer/memory manipulations, i really don't see the issue. I've written my share and I think its fine to add a roadblock if you want to shoulder the ability to add segfaults and other issues into a codebase. Additionally, it usually helps that you decide to encapsulate it into the least number of unsafe functions, instead of 'doing it manually' all over a codebase.
- nyanpasu64 4y agoRust is intended to be a systems language capable of replacing C++ in its niche, and interfacing with existing C++ at a fairly coarse-grained level like Firefox's oxidation (though cxx is trying to enable rich interop passing richer types than C-ABI ones), so it's trying to be a better screwdriver more so than a hammer. So difficulty expressing C++ concepts is arguably a flaw, and difficulty implementing all software with the same CPU/memory overhead as C++ (which I'd argue is the case, though some would disagree) is definitely a flaw. It's like creating a new screwdriver bit or handle, trying to convert the world to it, then attracting a legion of followers arguing that manufacturing flat-head screwdrivers should expose you to legal liability for anyone who slips it out of the socket and injures themselves (ignoring that flat-head screws existed and will continue to exist).
- cxr 4y ago> More important [...] the verbosity helps when reading code to know that something out of the ordinary is going on This applies to JS as well with its strict equality check (triple equals). Bad practices within the NodeJS ecosystem, however, have led to circumstances where triple equals has been cargo culted as the "right" thing to do for any equality comparison. The consequences of this include code that is more verbose, is no more type safe (and often doesn't do the right thing for some inputs—whereas with double equals, in contrast, it would...), and that the appearance of triple equals is no longer a strong signal that there's something happening that's worth paying attention to.
- adamddev1 4y agoCan you give some examples of inputs where === doesn't do the right thing?
- cxr 4y agoSuppose you're working on a compiler. You want to add a feature flag or otherwise be able to produce a build that when run attaches rich diagnostics information to the emitted tokens to assist in debugging—let's say you're really interested in the source offset where a given token appeared, for example. Unless you've already really gone off the rails in other ways, then you can trivially do this just by making special mode compiler pass around String objects where your token processing routines expect a string—making sure you define a `position` property at the time of creation. This is possible because your input isn't overconstrained. It's an abstract data type. Using triple equals, on the other hand, will screw this up. Your inputs are now overconstrained—they must be string primitives. In other words, you've invented an unnecessary requirement <https://www.teamten.com/lawrence/programming/dont-invent-unnecessary-requirements.html https://www.teamten.com/lawrence/programming/dont-invent-unn...>.
- adamddev1 4y agoWow that's a well thought out and interesting example, thank you. I recently learned about `new String()` and wondered where people might use/get burned by that.
- deleted 4y ago[deleted]
- mvf4z7 4y agoThis reminds me of Reacts "dangerouslySetInnerHTML" prop. https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml https://reactjs.org/docs/dom-elements.html#dangerouslysetinn...
- tmtvl 4y agoI've heard it called "syntactic vinegar" instead. I wonder if there's a term for syntactic useless stuff, like commas in Clojure quasiquoted lists.
- lioeters 4y agoI propose "syntactic konnyaku". > Konnyaku is a rubbery, flavorless zero-calorie food made of yams that are high in fiber and added to various Japanese foods for its squishy texture.
- Existenceblinks 4y agoFun read. I would love to read what if it's not text based, is it going to be different? Visual programming seems to suffer from composability and it's also bounded to be using human language as well, box with border is hard to comprehend, can get messy easily. I mean, visual but text-like theme. It seems to be in sweat spot. Only fix some downside/limit of text.
- csmeyer 4y agoShameless plug for my hybrid visual/text pl, Pickcode, which matches what you're talking about Demo programs: https://app.pickcode.io/playground https://app.pickcode.io/playground
- Vermeulen 4y agoWow this is awesome, love this style. I did something really similar with our game's scripting language called MBScript: https://docs.modboxgame.com/docs/mbscript https://docs.modboxgame.com/docs/mbscript Same kind of line setup, visual add button, etc. Whats Pickcode made for? Web programming?
- csmeyer 4y agoPickcode is meant for K-12 education as an alternative to block programming. The end goal is to have a WYSIWG editor for web apps with behavior defined using the visual programming language. MBScript looks great and I’d love to talk about your learnings from it. My contact is on the pickcode.io landing page if you want to chat!
- Existenceblinks 4y agoHey, yes! Nice, I'm thinking more serious and ambitious. You could go mass by adding "module" and ways to compose. The keyboard navigation is ok-ish (honest opinion) because this is the hardest ones which is design for every day task in long run, at minimum should be as fast as text based programming. At least 3-4 devs are comfortable to work on this codebase (non-realtime, just normal version branching flow) I really really want this decades old idea to take off. We should have grammar files in .. json is fine (have to start somewhere), and have spec for editor implementor to spread it across platforms. Ideally languages creators only have to customize "view" to decide how their lang would look like. Probably configure keymaps if they think their lang can be developed fast with certain keystroke (akin to emacs but more friendly because medium is not text anymore)
- JohnDeHope 4y agoI enjoy this sort of "one example, multiple different lenses" style of discussion. It reminds me of this book... Exercises in Programming Style by Cristina Videira Lopes.
- rramadass 4y ago>Exercises in Programming Style by Cristina Videira Lopes I didn't know of this book (have been looking for something like this for a while) and hence thanks for the pointer.
- wodow 4y agoI really like the term "Sugary Functional Style" -- sweetening pure functional programming with faux procedurality. Looks like it's a (three word) Google Whack at the time of writing: https://www.google.com/search?hl=en&q=%22Sugary%20Functional%20Style%22 https://www.google.com/search?hl=en&q=%22Sugary%20Functional...
- bhauer 4y agoIt's awesome to see an article from Dr. Ray Toal on HN this morning! In my biased opinion, the excellent tenured professors at LMU's CS program make it a stand out for its size.
- tabtab 4y agoI actually like the VB-style, but VB did it mostly wrong: if you start the block with X, you should always end it with "End X". Thus, you'd have While ... End While instead of crap like "Wend" and "Next" (in For...Next). It's more legible to know what block is being ended. C-style continually frustrates me that area. The End-X style just never found a nice way to wrap text for longer statements. C-style also has a problem in that there is no way to define arbitrary blocks: it relies too much on key-words. I'm trying to remedy this with "Moth" syntax: https://www.reddit.com/r/ProgrammingLanguages/comments/ky22dx/simplified_take_on_moth_colonfree/ https://www.reddit.com/r/ProgrammingLanguages/comments/ky22d... It's LINQ-esque but without the bloated Lambda conventions, and influenced by XML in that you have a simple syntax pattern that can "implement" many domains' needs. It started with an attempt to merge the best of Lisp and C-style. (Whether it succeeded or not is hotly contested. I welcome other attempts.)
- f1shy 4y agoNote that in the for case, you can have many "next" and having many "end" would be silly.
- Jtsummers 4y ago> I actually like the VB-style, but VB did it mostly wrong: if you start the block with X, you should always end it with "End X". Thus, you'd have While ... End While instead of crap like "Wend" and "Next" (For...). That's covered in their syntactic salt section, but Ada does, mostly, what you describe. procedure Proc(...) is -- vars, types, and subprograms defined here begin ... end Proc; for I in Some_Array'Range loop ... end loop; if condition then ... end if;
- mojifwisi 4y ago> C-style also has a problem in that there is no way to define arbitrary blocks I might have misunderstood what you mean by "arbitrary blocks", but you can definitely do this in C: int main() { { /* arbitrary block */ return 0; } }
- Jtsummers 4y ago
- adamnemecek 4y agoSeems down https://web.archive.org/web/20221018135106/https://cs.lmu.edu/~ray/notes/syntaxdesign/ https://web.archive.org/web/20221018135106/https://cs.lmu.ed...
- mncharity 4y agoAnother exploration of syntax: http://rigaux.org/language-study/syntax-across-languages.html http://rigaux.org/language-study/syntax-across-languages.htm...
- xaedes 4y agoWow, amazing! This really is a comprehensive overview. Basically a syntax Tafelwerk.
- djedr 4y agoVery nice little article! Learned some new terms. To anybody dabbling as I do in syntax design, who may be looking for an extremely minimal representation for trees (even more minimal than S-exprs!) I would like to introduce my little project called Jevko: https://djedr.github.io/posts/jevko-2022-02-22.html https://djedr.github.io/posts/jevko-2022-02-22.html It is pure distilled treeness. Its grammar fits into one line, if compressed well: Jevko = *("[" Jevko "]" / "`" ("`" / "[" / "]") / %x0-5a / %x5c / %x5e-5f / %x61-10ffff) This took me years of syntax golfing to figure out. I think it's turned out pretty nice. It's complete, formally defined, with a few simple parsers written, except it has no users. ;D To relate back to the article, an interesting and AFAIK original feature of this syntax is that newlines or other whitespace are neither significant nor insignificant nor "possibly significant" in Jevko. I'd call it whitespace-agnostic. Various whitespace rules can be laid on top of it, producing for example a Lisp-like language with native multiword identifiers with spaces, e.g.: define [sum primes [[a][b]] accumulate [ [+] [0] filter [ [prime?] enumerate interval [[a][b]] ] ] ] here "sum primes" and "enumerate interval" are two double-word identifiers. It's the only right_solution to the identifierWars, I-tell-you!
- nathell 4y agoI thought “what a weird name”, then silently pronounced it and my Polish ear heard “drzewko”, meaning “little tree”. What a fitting name. :)
- djedr 4y ago:) For a long time I couldn't find the right name that would express the generic nature of it. An earlier prototype was called TAO, as an acronym for Tree Annotation Operator (it had an extra feature called operators), and as a reference to the ancient Chinese concept, in essence nameless and by design hard to pin down -- this seemed to fit perfectly. However there is about 2^42 cowznofski potrzebie things called TAO (kind of ironic, as the original idea was that the Tao would be distinct from the countless named things), so it turned out to be a bad name after all. So I decided to find a more unique one and here we are. The amount of time spent thinking about this and the lengths I went to are better left untold. In other words, naming is hard.
- samsquire 4y agoI began designing a language that handled recursion and iteration as relations between variables which are topologically sorted to determine control flow. Each function is a toplogical graph of stream functions so it is similar to a data flow language or reactive programming language. The goal is that you should express the critical insight of the algorithm to work out what to write and the code is not nested so there is very little tree structure. Algebralang is rough notes on how it would appear. https://github.com/samsquire/algebralang https://github.com/samsquire/algebralang Example programs in the repository are binary search, btrees, a* algorithm. I wrote a multithreaded parallel actor interpreter in Java and it uses an invented assembly language which doesn't have a bytecode, it's just text. https://github.com/samsquire/multiversion-concurrency-control#parallel-interpreter https://github.com/samsquire/multiversion-concurrency-contro... I like the ideas behind ani language https://github.com/waves281/anic https://github.com/waves281/anic
- Jtsummers 4y agoIf possible, I'd add indentation to your examples to make them much more readable. As it stands, it's like reading one of my math prof's C code (he was an old FORTRAN when it was shouting coder and never learned to indent): insert t node = recursive_deepest_first(items=t.children,item=t, lastRecursion=l)( if len(t.children) == 0 t.activate() location = reversed(t.children).find(item=i, item.value >= node.value) output = insert t node if len(t.children) > 3 { replace(t, Node(value=t.children÷(point=middle=m) = m.value,children.sort(item=i,sortKey=i.value)=t) output = l.t } else deepest t.children.append(node) ) Assuming this doesn't invalidate the program it reads more clearly and only takes one more line: insert t node = recursive_deepest_first(items=t.children, item=t, lastRecursion=l)( if len(t.children) == 0 t.activate() location = reversed(t.children).find(item=i, item.value >= node.value) output = insert t node if len(t.children) > 3 { replace(t, Node(value=t.children÷(point=middle=m) = m.value, children.sort(item=i, sortKey=i.value)=t) output = l.t } else deepest t.children.append(node) ) That original program was hard to decipher for both lack of indentation and the odd line breaks, and inconsistent choice of space or no space after commas. Another question, why do you use `if ... then...` in some examples and not in others? Is that a user choice?
- hzhou321 4y agoThe `infix` syntax is missing from the major items. Without infix syntax, all languages are just variations of LISP -- I guess that was all the article is about.
- Arch-TK 4y ago> Because C does not have real arrays C does have real arrays, they just get implicitly converted to pointers to their first element in a lot of cases (for a multitude of reasons in part having to do with simplifying the language), A[B] is defined as such so it works with normal pointers and arrays-converted-to-pointers in the same manner. Try using an array with sizeof, unary &, or in the form of a string literal used to initialise an array. In those situations it suddenly stops behaving like a pointer to its first element and definitely behaves like something which is unlike anything else in C (hint: it's an array).
- kragen 4y agoAnother interesting weird syntax I ran across a few years ago is OGDL: https://ogdl.org/ https://ogdl.org/ It's sort of an alternative to S-expressions with much less punctuation, but the data model is slightly different — in S-expressions you label the leaves, and in OGDL you label the nodes. In other contexts these node-labeled trees are sometimes called "rose trees"; they are the basic data model of, for example, Prolog. Labeling nodes is almost equivalent to labeling arcs, but OGDL does support multiple references, so not quite. The OGDL proposal was intended for data, like XML, not programs. They started out by trying to simplify YAML, which has arrays and dicts, and they simplified it by unifying them into a single structure. Here's one of their examples: network eth0 ip 192.168.0.10 mask 255.255.255.0 gw 192.168.0.1 hostname crispin This is not quite just an edge-labeled digraph because, as in S-expressions, the order of arcs within a node is significant; you can have multiple edges with the same label in the same node, and you can select edges by ordinal rather than, or in addition to, label. This is of course amenable to use as a programming syntax.
- Archelaos 4y agoI wonder what would be possible if we could move away from the text editor and code with an e-pen on a tablet, the way mathematicians use a black board, where the parsing is not from left to right, but follows specific paths in two dimensions (like the notations for fractions, square roots, sums, integrals, etc. we learned at high school). -- Has this been tried out recently?
- emmelaich 4y ago> [mark the end by] .. spelling the initial word backwards This reminds of the quirk in shell. if/fi, case/esac but ... do/done? It might be because `od` was taken for octal dump.