Live data from Hacker News

Use boring languages with LLMs

jry.io

161–170 of 180 posts

Re: Use boring languages with LLMs

#161
Gleam is a new kid, but it seems to fit this trend: - Less ways to write code; - Strongly typed; - Erlang parallelism; - Exhaustive pattern matching; - No nulls (and many other stuff); - Pipelines (not sure about LLMs, but it fits my eye);

Re: Use boring languages with LLMs

#163

Just want to throw the other Google language into the ring. While I would say Dart has a few more fancy language features than Go, it has an extremely strong and modern cli tool, which is a one stop shop for all your formatting, linking, and project building needs. It even grades how well your project is constructed before you publish it to pub.dev

What does it bring to the table that isn't achievable with Cargo?

I just feel like Dart doesn't offer enough for a fairly standard OOP lang which isn't particularly fast and doesn't have the library ecosystem or vast training data of Rust, Go etc

Re: Use boring languages with LLMs

#164

Earlier quoted context omitted.

In practice, Ruby is much more opinionated than Perl, mostly because of Rails being the catalyst to drive mass adoption of Ruby. So you basically have Rails, and then the RubyGems/Bundler package management was pretty linear. There's no equivalent of python2->python3 schism and the package management churn, and more stylistic continuity since ruby adoption is lower and more concentrated in the 2005-2015 time frame co…

> There's no equivalent of python2->python3 schism and the package management churn I tried to run a Ruby script recently and got an error about Fixnum. Apparently they made some breaking change to how integer types are referenced in version 3. I had to modify the script to get it to work on a modern parser. How is this not equivalent to the Python 2-3 jump? I don't know the first thing about Ruby but this already to…

>How is this not equivalent to the Python 2-3 jump?

The same way triping on a small pothole is not the same as falling into a giant sinkhole.

The claim was not that Ruby had no incompatible changes whatsoever.

Re: Use boring languages with LLMs

#165

When you're working on something difficult that requires a model to reason intelligently, lower level and strongly typed languages often outperform on the same problems [0]. We have a few hypotheses about why, with a moderately high correlation between performance and token density of the output program -- i.e. more token dense languages are more difficult for programs to reason about. Most models come up with the le…

Even though Python code may use more characters/LoC than say Rust in text form, it's not necessarily more token dense because LLM tokenizers are good at "compressing" its English keywords

In contrast, langs with symbol-heavy syntax (ALP as extreme example) use fewer characters but don't tokenize well in practice so aren't as efficient as one would think

Re: Use boring languages with LLMs

#166

Earlier quoted context omitted.

> > everyone knows goto was bad. > Absolutely hard disagree. You can write extremely clean and resilient C with C89, goto, and a handful of rules. That's a different goto. The one in C89 can only jump around within functions, but the article is talking about goto that can jump between any two points in the whole codebase arbitrarily. It stresses that point a bit more later on in the article, but you can already see i…

Here is a structured cross-codebase GOTO for C++, with optional declared & typed continuation values, sub-tasks/states/state-machines, and optional delegated typed termination values. // Syntax: { ...; y = go_to state1(x, ...); } // Meaning: Cross-codebase GOTO w/continuation values // Implementation: tail call #DEFINE go_to return // Syntax: { ... y = go_do state2(x, ...); ... } // Meaning: Cross-codebase sub-task/s…

That's not the same: you have to cooperate in the game, so if you have a library that uses it internally and you call one of its functions in the normal way then it will still just return as usual. So it still provides the usual guarantees of function calling, even if you don't want it to.

(It also doesn't allow you to jump into the middle of a function, or to take more than a finite number of steps unless you're using a compiler with guaranteed tail call elimination.)

Re: Use boring languages with LLMs

#167

Just want to throw the other Google language into the ring. While I would say Dart has a few more fancy language features than Go, it has an extremely strong and modern cli tool, which is a one stop shop for all your formatting, linking, and project building needs. It even grades how well your project is constructed before you publish it to pub.dev

What does it bring to the table that isn't achievable with Cargo? I just feel like Dart doesn't offer enough for a fairly standard OOP lang which isn't particularly fast and doesn't have the library ecosystem or vast training data of Rust, Go etc

> What does it bring to the table that isn't achievable with Cargo?

Speaking purely about the cli dart devtool (flutter and debugging) dart compile (More options but the big one is JavaScript transpilation) dart info (outputs diagnostic information about installed dart toolchain)

> which isn't particularly fast

Against other programming languages that are also garbage collected Dart compares favorably, though it's implementations of things typically aims for user accessibility over maximum performance. For example a Dart Isolate is much more heavy weight than a goroutine due to it's higher startup cost and additional memory per worker. The advantage is that it has its own memory, own event loop, and communicates with other isolates only by message passing, so it's much harder to mess up.

But where Dart shines for me is in two instances. The first is as a Python programmer that wants to work in a statically typed, ahead of time compiled language. Dart fits the bill here, and covers a weakness many programming languages have which is user facing GUIs. I would argue nothing comes close to Flutter when it comes to slapping together cross platform GUIS that just work. Dart is also very good on the front and backend (with more emphasis on the frontend) of web development, and can be integrated into an existing project very well through it's ability to compile into JavaScript. Typescript definitely eats Dart's lunch here, and Go more so for the backend, but very few languages can do Webdev in a way that feels natural and due to many design decisions of the past Dart is better than most. So Dart is good for packaged user facing apps.

This also makes it a strong contender as the higher level language to pair with a lower level language. You mentioned Rust, and Dart has a Flutter Rust Bridge^1 which allows you to write normal Rust code that automatically has the glue code generated. The native code interaction story gets even better through Dart's Hooks^2, Which allow you to integrate and compile or download native assets into your Dart projects directly through Darts build interface.

> doesn't have the library ecosystem or vast training data of Rust, Go etc

LLMs are better at handling niche languages than they have ever been. And the fact that Dart is not applied broadly, in the same way Python is, means you don't need a million examples to cover the most common use cases.

1. https://pub.dev/packages/flutter_rust_bridge 2. https://dart.dev/tools/hooks

Re: Use boring languages with LLMs

#168

Earlier quoted context omitted.

& and &mut is all over the code in Rust. &T by far the most common arg type. In the OP article, they mention 'don't need to worry about thread cause the concept does not exist' - well, & does not exist in Python. Those things are related to low-level computational issues (memory management) not problems space issues (moving money, transcoding the file, checking the spreadsheet), so a lot of &/&mut etc. and all that e…

There is a huge semantic difference between sharing an object by providing a reference to it, passing the ownership of the object and passing a copy. It’s not a technical low level detail because the results can differ. Many developers use those distinctions in Rust to encode business rules, not to optimize memory use. Similarly there is an important semantic difference between something you can change and something…

"There is a huge semantic difference between sharing an object by providing a reference to it, passing the ownership of the object and passing a copy."

Yes - of course there is!

You're totally right!

But that semantic difference does not exist in most languages.

For good reason.

That 'semantic difference' only matters in a specific context - wherein you need to have the difference, usually for performance reasons.

There's no need to expose that semantic difference in the surface language, in most cases.

And that 'semantic difference' requires an enormous amount of thinking to process across the system. It's a huge amount of possibly unnecessary work.

Making the AI have to deal with that layer of design concern is a big cost you only want to deal with if it's worth paying the price.

Re: Use boring languages with LLMs

#169
post #93

Earlier quoted context omitted.

I love REPL-driven development, and exploratory "programming" via small snippets is likely part of the endgame (the more the agent strays outside its comfort zone, the more it's needed). It also looks like it paradoxically saves context, since piling up many small snippets is still better than trying to fix a one-shot gone horribly wrong. And yeah, if Clojure had a better static safety story, it would actually rank h…

It sounds almost squarely written by someone without practical experience of REPL-driven development. Specifically with Lisp REPLs. Sure, other languages also have REPLs, but if you dig just a bit deeper, you'd learn that every single step there in R[ead] E[val] P[rint] L[oop] has differences. That makes the entire holistic experience of using the language drastically different. Not universally better for every domai…

Your intuition is not right, I have plenty of experience with REPL-driven development, even having used all the tools you have mentioned in your post. (and Clojure Spec is exactly the runtime specification part I was referring to in my comment...)

Logically, it's more the other way around - REPLs are in that sense a compensation for static types, since nothing forbids having a REPL and static types (see: eg. Julia. OCaml, Haskell, Scala etc. have REPLs too, but they are closed-world).

There's a fundamental trade-off though, with the open- vs closed-world assumption - if your types and (generic) methods are open world, then certain kinds of errors are just not analyzable. (this is a more general mathematical truth, also occurring in eg. description logics and the Semantic Web)

As you hinted, REPLs also don't work well for eg. large pipelines farmed out to clusters of machines, or anything that requires a high degree of static assurance, eg. control software - you either must statically analyze for all errors for it to even be legal, or the cost of having to "live fix" an error is much larger than preventing it in the first place (or just impractical for clusters).

Sure, a degree of live patching and experimenting is desirable (namedrop JPL as Lispers do), but that's a somewhat orthogonal concern.

A Lisp-style REPL combines introspection and live patching for both code and data. This can be achieved in many heterogenous ways besides a REPL, though. It's just not a nice, curated and holistic experience. But agents don't really care about that. LLMs don't get magic feelings. They can easily work around minor annoyances and a lack of ergonomics (assuming it doesn't burn context).

(Since LLMs tend to leave junk behind and slowly corrupt everything over time, a persistent image is also not necessarily a good thing. Reproducibility is also harder.)

The Unix philosophy "everything is a file" can ultimately also be viewed as image-based programming. [1] The system is the image.

As long as you have a way to address (via files, namespaced symbols, UUIDs, URLs, numbers etc.) the code, the execution state (ie. the stack frames) and memory (ie. the heap of objects), it has the same effect.

The problem with most software is that its internals are not addressable. I don't think one has to use eg. Lisp to fix that though. I do acknowledge that most static languages fail here.

[1]: "Unix, Plan 9 and the Lurking Smalltalk" https://www.humprog.org/~stephen/research/papers/kell19unix-...

Re: Use boring languages with LLMs

#170
post #80

Author here, wasn't expecting this piece of writing to show up on HN. The specifics of Python were chosen only due to the language ecosystem being fragmented and inconsistent while Python remains an essential learning, research, and now ML programming language (it was my first language and I still love it). My thoughts on LLM generated code have changed immensely in the last 9 months as I've taken on teams and projec…

> Languages with a single way to do things benefit the most: Rust I posit that Rust is the optimal language to emit from LLMs unless you have to target web, a specific platform, or a legacy project: - The required error handling for Option , Result , and required destructuring of sum types naturally reduces errors by an order of magnitude - If it compiles, chances are higher the code is correct. Especially if you're…

Even for the web, Rust is a great language out of LLMs. It was quite surprising given the early performance of Python that Rust does so well. It really speaks to the high dimensional generality of transformer translation models.
Post reply on HN