Syntax is a UI problem, but programming languages are more than syntax sugar for assembly. Features like Rust's borrow-checker, type systems, even small things like name resolution and async/await provide "semi-formal"* guarantees. And LLMs are no substitute for even semi-formal guarantees; people have spent lots of money and effort trying to integrate (non-NN, rule-based) algorithms and formal methods into LLMs, some even writing LLM programming languages, because they may be the key to fixing LLMs' accuracy problem.
* Not formally defined or verified, but thoroughly defined and implemented via reliable algorithms. A true formal specification is completely unambiguous, and a true formally-proven implementation is completely accurate to its specification. A thorough language specification or reference (like https://tc39.es/ecma262/ or https://doc.rust-lang.org/nightly/reference/) is almost unambiguous, and a well-tested compiler or interpreter is almost always accurate, especially compared to LLMs.
Cases where true formal guarantees matter exist but are rare. There are many, many cases where LLMs are too inaccurate but well-tested algorithms are good enough.
"UI" issues, while not at all unimportant, are surface level. Semantics are fundamental. Get the semantics of your language wrong, and you are stuck. Indeed, this is what happens with most popular languages that aren't well-considered:
- lang author creates some random new lang, throws whatever feels like a good idea to them into the lang
- lang community discover that these features interact in rather surprising ways, ways that keep tripping up newcomers to the language, and would like to change it.
- lang author, having had X more years of experience now, identifies that changing the behavior could break user programs who rely on that behavior (wittingly or un-)
- lang author decides to write a new, backwards-incompatible version, community has 20 years of turmoil before recovering (python3), if it ever does (perl6).
A human view of programming languages - Amy J. Ko
In computer science, we usually take a technical view of programming languages, defining them as precise, formal ways of specifying a computer behavior. This view shapes much of the research and development that we do on programming languages, determining the questions we ask about them, the improvements we make to them, and how we teach people to use them. But to many people (even software engineers) programming languages are not purely technical things, but socio-technical things. In this talk, I discuss several alternative views of programming languages, and how these views can reshape how we design, evolve, and use programming languages in research and practice.
The upshot is while the "PL as math" and "PL as tool" views have been well researched, if you're a researcher getting started in this area there is plenty of fertile ground out there. We might just have this view that "PL is math" because we've done the most work there, but there are many more applications of programming languages we have not thoroughly explored enough to say they are not just as important.UI is still important, but I do not think it really defines a language.
Let’s say I created a new Haskell compiler, and it had the best error messages in the world, a hiccup-free LSP experience, maybe an alternative syntax format that’s more familiar to mainstream devs. But it still has all the same semantics. Is that still Haskell? Why or why not?
Now let’s say I took whatever has the best tooling currently, idk what it would be, maybe TypeScript, but gave it say non-strict semantics. Is that still typescript?
If you take it to an extreme, like with Smalltalk, you wind up with languages whose environment is considered essential, they are basically part of the language at that point.
To most people it is about IDE and development tool chains.
Or we can think of it from the perspective of mapping programmer’s mental model to the problems they want to solve. Interface to the Mind, so to speak.
I have been intentionally coding with Claude in the past few months and I started to think about programming from problem solving standpoint other than generating artifacts that conform to whatever languages, libraries and frameworks I happen to use to solve the problem at the time.
We can call this mental model or not. But it seems to be a different abstraction than we are talking about at PL level.
To me, LLM is part of the interface between me and the solution, that is what I mean by UI.
The real work is still in my head. PL language is just a medium.
Serious (germ of a) question.
A bad idea, probably. LLM output needs to be reviewed very carefully; optimizing the language away from human review would probably make the process more expensive. Also, where would the training data in such a language come from?
In the end, we circle back to lisps, once you're used to it, it's as easy for humans to parse as it is for machines to parse it. Shame LLMs struggle with special characters.
Also feels like making sure the tokeniser has distinct tokens for left/right parens would be all that is required to make LLMs work with them
But I'm having way more "unbalanced parenthesis" errors than with Algol-like languages. Not sure if it's because of lack of training data, post-training or just needing special tokens in the tokenizer, but there is a notable difference today.
> shame LLMs struggle
That sounds like Stockholm syndrome more than an easy-to-use language.
But don't take my word for it, ask the programmers around you for the ones who've been looking into lisps and ask them how they feel about it.
edit: Lisp -> s-expressions
- memory safe, thread safe, concurrency safe, cancellation safe
- locality of reasoning, no spooky action at a distance
- tests as a first class feature of the language
- a quality standard library / reliable dependency ecosystem
- can compile, type check, lint, run tests, in a single command
- can reliably mock everything, either with effects or something else, such that again we maintain locality of reasoning
The old saying that a complex system that works is made up of simple systems that work applies. A language where you can get the small systems working, tested, and then built upon. Because all of these things with towards minimising the context needed to iterate, and reducing the feedback loop of iteration.