Because the language gives you many different tools, an LLM generated codebase can get inconsistent and overly complicated quickly. The flexibility of Python is a downside when you’re having an LLM generate the code. If you’re working in an existing codebase, it’s great - those choices were already made and it can match your style.
When an LLM has to derive its own style is when things can devolve into a jumbled mess.
What language do you feel is easier to reason about in the large?
I think I have never seen haskell software made wih LLM's but well, aside from university, I have not seen Haskell code at all. (Also Haskell purists I would associate with people who avoid LLM's)
I would rather go with Rust given these choices.
But I have good results with typescript (or javascript for simpler things). Really large set of examples. Tools optimized for it, agents debugging in the browser works allmost out of the box. And well, a elaborate typesystem.
Compared to most languages, including Java, C# will have a hard time letting you compile incoherent code.
You barely need any dependencies other than aspnetcore and efcore for most applications and your AI knows them well.
It’s easy to do TDD with it so it’s easy to keep your IA from hallucinating.
> There are not that much different ways to get somewhere
This is far from true. C# is a language where you can operate on the raw pointers through unsafe keyword. On the other end of the spectrum, you can have duck-typing in dynamic blocks.
For operating on collections you can use old style loops, or chain of lambdas or sql like syntax.
I have been coding in C# old school way for most of my life at this point, and I feel like I'm in a foreign land reading code from some other C# projects.
You'd have to steer the LLM to use the style you want, and not massively overarchitect things though, but that's going to be an issue nonetheless.
Do you have any recommendations for systems where reasoning about large systems is easier than in python?
As a rule, I avoid implementation inheritance. Occasionally I need to facade a library that assumes implementation inheritance to avoid it spreading into my codebase.
When the codebase hits a certain size, I hand-roll some decorators to create functionality like java interfaces. With that done, and a suite of acceptance tests, I find it scales up well.