Laurence Tratt, a computer science researcher, writes about programming language implementation, VMs, parsing, and benchmarking methodology.
HN links his posts on parsing being an unsolved problem and on why minimum times mislead in benchmarking, both cited as clear explanations.
> new books should use more modern popular languages to age well and to attract younger audience
That doesn't make sense. Java is one of the most popular programming language, more popular than a lot of other "modern popular languages". The only languages more popular are either as old as Java (JavaScript, Python) or older (C, C++). C# might be one option, but it's close to Java, and I don't know if the support for Linux/MacOS was here at the time this book was started. Same thing for JavaScript and Python's popularity, which are relatively recent trends. On the other hand, Java has stayed and endured. It's still here and will still probably be here in 20 years.
> on the JVM there are languages better suited for the topic (e.g. Scala by a large margin)
Scala is less popular than Java, and harder to master. Once you consider Scala, you get into endless arguments about whether to use Scala, SML, OCaml, Haskell, etc.
> There's already the "Language Implementation Patterns" book
There is also "Writing an interpreter in Go" and "Writing a compiler in Go" that seem to cover the same ground. But I'm certain than more people know about interpreters and compilers thanks to these three books than thanks to any one of them.
> but also gives you real life skills (e.g. ANTLR is used in most big data query engines)
The majority of programming languages don't use a generated parser but a handmade recursive descent parser, because it's the best at error reporting. There's a good post…
LSPs rely on a parser to generate an AST for a given language. This parser needs to be error-tolerant because it needs to return usable ASTs despite often parsing incomplete, incorrect code and fast enough to run on every keystroke so it can provide realtime feedback.
Most of the time they rely on their own hand-rolled recursive descent parser. Writing these isn't necessarily hard but time-consuming and tedious especially if you're parsing a large language like C++.
Parser generators like yacc, bison, chumsky, ANTLR etc. can generate a parser for you given a grammar. However these parsers usually don't have the best performance or error reporting characteristics because they are auto-generated. A recursive descent parser is usually faster and because you can customize syntax error messages, easier for an LSP to use to provide good diagnostics.
Tree-sitter is also a parser generator but has better error tolerance properties (not quite as good as hand-written but generally better than prior implementations). Additionally, its incremental meaning it can reuse prior parses to more efficiently create a new AST. Most hand-written parsers are not incremental but are usually still fast enough to be usable in LSPs.
To use tree-sitter you define a grammar in JavaScript that tree-sitter will use to generate a parser in C which you can then use a dynamic or static library in your application.
In your case, this is useful because you can compile down those C libraries to WASM which…
PEGs are one class of cleanly composable grammars. OMeta uses that attribute of theirs to great advantage. And PEGs are, formally speaking, parsable in worst-case linear time, although whether that linear time is fast enough to be practical is still unknown, and it also potentially uses large linear space. I hope it's not vulgar to post yet another link to my minimal PEG parser generator in one page of code: https://github.com/kragen/peg-bootstrap/blob/master/peg.md
In particular, the example here of embedding SQL in C would be pretty trivial to do as a PEG.
PEGs can parse some languages that are not context-free — the example from Bryan Ford's thesis is aₓbₓcₓ, where xᵢ means "i repetitions of x". I think there are also context-free languages they can't parse, but I'm not sure.
(Operator precedence parsing is particularly ugly in PEGs, since by default, they don't support left recursion. The OMeta folks figured out how to cleanly support left recursion in PEGs, but you lose the linear-time guarantee.)
One very appealing attribute of PEGs is that you can conveniently extend PEGs to support parameterized productions, which could be crucial in building up a library of general-purpose parsing productions that could reasonably be used to shorten the time to define new languages.
k4st claims in http://news.ycombinator.com/item?id=2330672 that Pratt top-down operator-precedence parsers are also cleanly composable. I didn't know that, and it seems like a surprising claim,…
Robert Harper argues that dynamically typed languages are languages of a single Type:
And this is precisely what is wrong with dynamically typed languages: rather than affording the freedom to ignore types, they instead impose the bondage of restricting attention to a single type! Every single value has to be a value of that type, you have no choice!
http://existentialtype.wordpress.com/2011/03/19/dynamic-languages-are-static-languages/
Bob in this same article also wrote the now oft quoted:
To borrow an apt description from Dana Scott, the so-called untyped (that is “dynamically typed”) languages are, in fact, unityped.
Now the author of the linked article wrote another piece in 2012 that rebuts this:
It therefore makes no sense to say that a language is unityped without qualifying whether that relates to its static or dynamic type system. Python, for example, is statically unityped and dynamically multityped; C is statically multityped and dynamically unityped; and Haskell is statically and dynamically multityped. Although it's a somewhat minor point, one can argue (with a little hand waving) that many assembly languages are both statically and dynamically unityped.
http://tratt.net/laurie/blog/entries/another_non_argument_in_type_systems
It is worth noting:
Sam Tobin-Hochstadt argues the uni-typed classification is not very informative in practice.
The uni-typed theory reveals little about the nature of programming in a dynamically typed language; it's mainly…
My gut feeling is that one can minimally maintain systems in "forgotten" languages indefinitely if they're basically stable and feature complete. But it seems that people come unstuck for systems that still need to evolve -- even if that evolution is slow and/or sporadic. The obvious alternative is to rewrite systems in new languages, but that is generally prohibitively expensive. Even for those that can afford the expense, the new system tends to not to take considerable time to bed in, so flipping the switch from the old to the new is only for the brave or those with excellent PR departments.
Although we didn't have this in mind when we started on this work, I've come to think that language composition might offer a way out of this mess (I immodestly offer [1] as an early example). If one composes an old and a new language, one can migrate a system from an old to a new language bit-by-bit. As well as amortising the translation cost, it also means that one can minimise the "switch flipping" problem: disasters seem much less likely if the composed system can be introduced gradually.
[1] http://tratt.net/laurie/blog/entries/fine_grained_language_composition.html
I envy these small and steady improvements!!
I spent about one week implementing PyPy's storage strategies in my language's collection types. When I finished the vector type modifications, I benchmarked it and saw the ~10% speed up claimed in the paper¹. The catch is performance increased only for unusually large vectors, like thousands of elements. Small vectors were actually slowed down by about the same amount. For some reason I decided to press on and implement it on my hash table type too which is used everywhere. That slowed the entire interpreter down by nearly 20%. The branch is still sitting there, unmerged.
I can't imagine how difficult it must have been for these guys to write a compiler and succeed at speeding up the Python interpreter.
¹ https://tratt.net/laurie/research/pubs/html/bolz_diekmann_tratt__storage_strategies_for_collections_in_dynamically_typed_languages/