I strongly agree with the premise of this article, which is why I am surprised that the author moved away from Haskell to Python.
For some time now it’s felt clear (or at least extremely) compelling that agents need fast compile times in order to be effective, especially when you’re working in parallel. But the other thing that has felt just as obvious is that agents need strong type systems and narrow guardrails in order to constrain their outputs. These two things felt clear enough to me that, like the author, I wanted to choose a language ecosystem that maximized them. There _are_ languages that both have expressive type systems _and_ fast compile times. I wonder if the author investigated any of them, before deciding that no compilation time at all was acceptable.
In my case I landed in OCaml. I think there are other options in the space—Go if you want less typing but faster compiles; Rust if you want more types but slower compiles. My mostly vibes-based evaluation landed on OCaml, and I’ve been pretty happy with the results.
- None of us are experts in Rust, and we're all solid at Python.
- Rust felt like an under-correction for what we wanted (get all friction in front of the LLM out of the way).
- Our high-performance stuff is not being migrated at this time (Scarf Gateway), so we're just talking about basic CRUD backends here. Basically any language will work.
You might not like Microsoft but they did a video on why they re-wrote the last version of the Typescript compiler in Go. Basically, because of LLMs. It's worth viewing even if you don't decide to go with Go.
True that an interpreted language has a leg up on any compiled language in the arena of compile time, but worth noting that one of Go's primary design goals was improving compile times of massive code bases. Google was drowning under the weight of compiling huge C++ codebases and Go was the response to that (among other things).
Python is preferred because Python programmers are cheaper than other languages. Not because of any sort of technical advantages. Its literally the worse performing programming language in popular use. And it uses invisible characters in its syntax. Truly, it is the VHS of our industry.
Go compiles things at package-level granularity. You only need to recompile your reverse dependencies on making changes. Also there's build caching available out-of-the-box, as well as some support for test caching.
Using a frontier model you can combine it writing high-quality Rust and educate you in the process. You guys were too scared, needlessly.
But as others said: if Haskell was not giving you too much in terms of excellent typing system then maybe going for a PL with a much faster compiler like Golang would have been best for you.
I've gotten best results with LLMs generating Go, Java, and C# code as they have the best combination of strong type systems and fast or no compile times.
I used TS and Go mostly with LLMs and they are very good at them. Python has been fine too honestly. A surprise entry is Flutter/Dart. They are very good at it. I think it's a mixture of types, the good tooling and focused documentation.
All of these I run in a cli that has automatic LSP in it so that's a huge factor too. The agent is automatically told when there would be compile time errors as well as linter issues.
I don't think that Devs making those chooses but business and industry forcing devs to explore other options. One of the main issues with Scala currently that is no good selling story anymore for a business peoples. Even Rust story with 'memory safety' very fragile because not a lot of businesses see it as huge issue which worth additional effort (probably only MS backing helps there).
In the past it was lot of selling points that business could see:
- 'better java' - more velocity over java without loosing anything
- 'type safety / FP' - less errors that cost money in production (backed by 'Typesafe/Lightbend' company)
- 'akka' - platform for distributed/concurrency applications (in pre kubernetes era was big demand for it)
- 'spark' - initially Scala was only way to use it fully
- 'twitter' - startup success story
- and few more
But now nothing from this list has any real meaning for peoples making decisions currently.
Kotlin actually suffer from this too as 'better java' not that big deal when java constantly improving.
However Kotlin has Google's as godfather, want to develop for Android with the official tools? Kotlin it is.
And no, Oracle lawsuit hardly has a role into it, as Android is still mostly all about Java ecosystem, Gradle, InteliJ, Android Java gets updated even if sloooowly (Java 17 LTS currently).
Had this not been the case and it would be yet another JVM guest language.
Just to put it in perspective, most Scala programmers rejected Scala3 before it was even written. The Scala team, in their infinite wisdom with a collective 2 years of professional programming under their belt knew better. Nobody switched and Scala2 has been slowly dying with their neglect ever since.
PS The Scala team should have been fixing their type inference engine which lacked some important features and needed a better (Horn clause based) architecture. They did a complete rewrite instead.
Then you have Metals for VSCode InteliJ plugins, while the Eclipse plugin was dropped.
InteliJ plugin is much further than Metals, however there is the conflict of interests with pushing Kotlin instead.
Meanwhile most Scala shops have pivoted to also give feature parity on modern Java, and Kotlin, thus reducing the interest in using Scala in first place.
However as mentioned, they are doing cool stuff with capabilities at EPFL for Scala 3.
I'd argue that any type system that don't support enforcing non-nil/nullability is not strong in any way and probably worse than not having any type system at all as they give a false sense of security.
>agents need strong type systems and narrow guardrails
I read the second paragraph of linked article as saying close to the opposite of that, particularly,
"the model can often avoid the mistake before the compiler ever sees the code. And as the models get better, the relative value of catching every possible issue at compile time changes."
In other words, LLMs are much less likely than humans to make dumb, fat-finger mistakes, and, when they do, are able to catch and fix them more quickly, ergo the value of type checking has fallen.
Everything in the prior sentence is, obviously, highly debatable. But it felt like part of the premise.
My experience with Claude and Ruby, Python, Javascript is similar: it's pretty good at finding the array of strings that was passed to a method instead of an array of integers. Think about record ids coming from a JSON API call. Or the single value instead of an array. I don't remember which Python XML parser is fond of returning one or the other according to the cardinality of the sub elements. Anyway, not only it writes the code to handle those cases but it traces the code and it finds the bugs. So type checking at coding time and who cares about writing the type annotations. They would be probably good to speed up the code at runtime but none of my customers use them and none of them is concerned about the current response time of their systems.
Personally been experimenting in Lean 4. LLMs understand it, can be given simple rules to improve it. Typing is strong, proofs are solid, and it compiles quickly.
On the contrary, for a small rust project, I had to clean out 180gb of cargo nonsense from the last ~3 days worth of compiles on a single, narrowly focused topic branch.
The library situation might be funky, but I'm also learning Lean 4 by hand. The tooling & lsp integration is lovely.
Jane Street has unlimited engineers and money to do this.
At least from the sidelines, it looks like they've built/forked most parts of the OCaml ecosystem to make it work (OxCaml, Dune was built by then, custom profilers, custom networking hardware, etc).
Of course a lot of their work is open source and can be used, but I wouldn't assume that it would be worth the effort to do something similar with OCaml yourself. Maybe it is! LLMs go brrr and all that.
I also went through quite a process to select a language to work with LLMs[1] before settling on OCaml.
I am not unhappy with the choice and find it works quite well, with relevant skills loaded, but I am always interested in others’ experience and understanding what they’ve discovered works well.
I was very happy with GLM 5.1 and Kimi 2.7.
I did see an improvement in GLM 5.2. I found it to be more realistic about what was and wasn't good architectural choices.
I have a pretty elaborate harness setup, I use my own spec DSL to plan and execute code. So what works for me may not totally apply to others.
I haven't used the frontier models in a few months, so I can't speak at all to this latest batch. When I switched over from frontier I found that what I lost in training data, I more than made up for with Pi harness.
Your process seems more thorough than mine. Mine was literally "I like camels". Switching over to OCaml I found wonderful - I had not worked with FP before and I found it delightful, it kind of cured my programming burnout a bit. I hand built a CRUD app in OCaml/Dream but still feel like a rank beginner, especially when it comes to actually writing code, lol. But I'm getting much better at reading it.
I think the typing and structure of OCaml is a great fit for LLMs, but also the proximity to other things like DSLs, formal proofs, and algebraic structures has tons of untapped potential.
I'm having an absolute blast with it. I like programming again.
One more thing - just seeing your affinity for Julia. I built a fully interpreted Lisp in OCaml which you might find interesting. I added my email to my profile here, feel free to reach out.
LLM are string happy instead of using ADT with ocaml.
That was my main pain point with it. Otherwise fairly happy, I would feel C#/.net core would be the best overall in term of language/platform.
I came away with the exact same conclusion as you. The article describes a rather commonplace career of fraud; more or less every industry has them. There’s nothing unique to classical music here; it’s human nature to generally take people at their word about their past accomplishments, to be impressed by famous names, and to allow their critical judgment to be influenced by what they think they know.
I think the author did quite well at explaining how Classical music's industry engenders this though: Foision was literally being given awards and prizes based solely on his made-up CV rather than his actual music. Sure that's not inherently unique, but it was convincing enough to me that there are structural problems inherent to how credential-and-"fame" focused the classical industry is, based on the articles arguments.
That said, I think the fraud itself is small, so perhaps the conclusion is too grand; but it's not out of nowhere either, I'm somewhat sympathetic to the arguments made.
Hi all—I'm the EM for the Search team at Notion, and I want to chime in to clear up one unfortunate misconception I've seen a few times in this thread.
Notion does not sell its users' data.
Instead, I want to expand on one of the first use-cases for the Notion data lake, which was by my team. This is an elaboration of the description in TFA under the heading "Use case support".
As is described there, Notion's block permissions are highly normalized at the source of truth. This is usually quite efficient and generally brings along all the benefits of normalization in application databases. However, we need to _denormalize_ all the permissions that relate to a specific document when we index it into our search index.
When we transactionally reindex a document "online", this is no problem. However, when we need to reindex an entire search cluster from scratch, loading every ancestor of each page in order to collect all of its permissions is far too expensive.
Thus, one of the primary needs that my team had from the new data lake is "tree traversal and permission data construction for each block". We rewrote our "offline" reindexer to read from the data lake instead of reading from RDS instances serving database snapshots. This allowed us to dramatically reduce the impact of iterating through every page when spinning up a new cluster (not to mention save a boatload in spinning up those ad-hoc RDS instances).
I hope this miniature deep dive gives a little bit more color on the uses of this data store—as it is emphatically _not_ to sell our users' data!
This is a fantastic post that explains a lot of the end product, but I'd love to hear more about the journey specifically on denormalizing permissions at Notion. Scaling out authorization logic like this is actually very under-documented in industry. Mind if I email you to chat?
Full disclosure: I'm a founder of authzed (W21), the company building SpiceDB, an open source project inspired by Google's internal scalable authorization system. We offer a product that streams changes to fully denormalized permissions for search engines to consume, but I'm not trying to pitch; you just don't often hear about other solutions built in this space!
Curious - what do you guys use for the T step of your ELT? With nested blocks 12 layers deep, I can imagine it gets complicated to try to de-normalize using regular SQL.
(I’m not on the search team, but I did write some search stuff back in 2019, explanation may be outdated)
The blocks (pages are a block) in Notion are a big tree, with your workspace at the root. Some attributes of blocks affect the search index of their recursive children, like permissions: granting access to a page grants access to its recursive child blocks.
When you change permissions, we kick off an online recursive reindex job for that page and its recursive subpages. While the job is running, the index has stale entries with outdated permissions.
When you search, we query the index for pages matching your query that you have to. Because the index permissions can be stale, we also reload the result set from Postgres and apply our normal online server-side permission checks to filter out pages you lost access to but that have stale permissions in the index.
Ok, that phrasing is mostly just to keep the pattern going. But I do want to bring to light the fact that there are actually a larger class of games of the same sort as Bridge - imperfect information, high skill, with a body of strategy and discussion - than most Americans are aware of.
I think it’s worth mentioning these for two reasons:
1. They’re really wonderful games! And they have deep cultural roots, which can be added delight for those of us who enjoy engaging with other cultures.
2. Bridge players can be kind of… dicks? That is, it’s unfortunate but true that the culture of Bridge can often be quite rigid and unfriendly. Especially to newcomers. As mentioned elsewhere, a surprising amount of Bridge has to do with the conventions encoded in the bidding, and if you don’t know those conventions you might feel rather lost, and your partner might get very annoyed at you.
Luckily, there are other games in the world that are the ‘Bridge’ of their own countries of origin - deep, strategic, rewarding years of play and study - that the average English speaker has never heard of.
I won’t go into too much detail but some highlights are:
- Preferans, a straight-trick-taking game for three from Russia;
- Skat, a point-trick-taking game from Germany;
- Tarocchino, a point trick taking game played with a 62 card tarot deck from Bologna;
- Koenigrufen, a point trick game played with a 54 card tarot deck from Austria;
- Danish tarok, a point trick game played with a 78 card deck;
- Vira, a straight trick taking game from Sweden;
- a half dozen incredibly deep and challenging games from Hungary alone. Something in the water over there.
In point of fact, Bridge is quite interesting, especially if you’re interested in the meta game of communicating through bidding conventions. I am not; there are other games out there that have really interesting features, lots of strategy, and a history dating back hundreds of years. Check them out!
Because I’m an annoying evangelist for this sort of thing, I’ll make sure my email is in my profile in case you’d like to know more.
Adding Doppelkopf a 48 cards combined half decks game. Played by 4. Teams 2 on 2 determined throughout the game. The game was influenced by Skat and Schafkopf and borrows from them. It is however very casual usually and the rule variations depend on the region you are in. Anyway to sum up trick an bid games are really fun!
Fyi for the US there is also binocle that has some traction and at least to my experience has been largely forgotten in the originating areas of Germany.
So happy to see tarot games mentioned on HN, I'm a big fan of their strategic depth and centuries-old cultural background.
Something a lot of people don't realise is that when tarot cards were invented, their intended purpose was to be a game. The whole divination/cartomancy aspect was made up much more recently, mostly to amuse French aristocrats.
I'm a huge fan of Hanabi. It's a cooperative game with imperfect information, both because you only know other players' hands (not your own) and because you don't know what order the rest of the cards will come in. But with good strategy and good communication (within the bounds of the game's restricted communication) it's possible to win just about every game.
There is ordered bidding that often conveys information, further information can be conveyed by playing style (if you hold an advantagous card in betl you use the cards in descending order, otherwise you use descending order) and lots of counting and memorization is involved. Both for the current game as for the next ones - cards are not shuffled only cut - so skillful players can reconstruct approximately who has what cards based on the previous game.
On the other hand it's most often played in a loud pub with a beer in hand so lots of mistakes are made.
It's also referenced frequently in literature esp. from 1st half of 20th century (Hasek, Polacek)
I’ve written a fairly long literate program in Literate: https://blog.zdsmith.com/whist.html I really enjoyed the experience. It came out well and it was relatively painless to compose.
Yes, in the first place there's no defined semantics and thus no meaning.
It's also not particlarly appropriate to claim that the language is 'unambiguous'. The intention of the language is to create a notation that allows for the unambiguous expression of relations that would be ambiguous in English, that is, to present a logical or mathematical specification rather than a natural language one. So the ambiguity or lack thereof is entirely in the mind of the reader.
I do have a semantics in mind for it; I have thought about things like operator precedence and currying and stuff. And that matters because you want to be sure that the person reading the specification and the person writing it had the same thing in mind as far as the order of the marks on the page goes.
So there will hopefully be a much more concrete realization of those semantics in a way that still leaves wide open the question of expression evaluation. In other words, it should be an objective fact that `a b c` is evaluated as `a (b c)` - and indeed it is, because the program has parsing rules for function application - but the specifics of what it means to apply `b` to `c` will never be enforced or evaluated on the machine level.
---
In fact, the parent comment's example is a good one because `fib n - 1 + fib n - 2` would be parsed as `fib (n - (1 + (fib (n - 2))))` which is not the intended reading. Right now, that's fine, because it doesn't actually evaluate them. It just pretty prints them back out in the right order and trusts that if you didn't put parentheses there, then the intended meaning should be clear enough to the human reader.
So what, ultimately, will be the relationship between the parsing rules in the program and the semantic openness of the language? Similarly, if I've decided that the language should have implicit currying, such that `f a` where `f` is a function of two arguments, should resolve to a partially applied function of one argument - does that have any meaning aside from a sort of 'suggested reading'? One potential direction for things to evolve is that the language will consist of a set of rules which are enforced by the interpreter and will result in a syntax error if not followed, and a set of rules which are explained in the documentation as a suggested convention but enforced nowhere. In that case the question of something like function precedence is still an odd grey area where the parser does have rules associated with it, but the symbols can flow in and out of the parser without being affected by those rules.
For some time now it’s felt clear (or at least extremely) compelling that agents need fast compile times in order to be effective, especially when you’re working in parallel. But the other thing that has felt just as obvious is that agents need strong type systems and narrow guardrails in order to constrain their outputs. These two things felt clear enough to me that, like the author, I wanted to choose a language ecosystem that maximized them. There _are_ languages that both have expressive type systems _and_ fast compile times. I wonder if the author investigated any of them, before deciding that no compilation time at all was acceptable.
In my case I landed in OCaml. I think there are other options in the space—Go if you want less typing but faster compiles; Rust if you want more types but slower compiles. My mostly vibes-based evaluation landed on OCaml, and I’ve been pretty happy with the results.