Hacker News new | ask | show | jobs
by necovek 6 days ago
I am not so sure that's this simple.

No matter your preference, programs in dynamically typed languages are still very much deterministic.

To be able to reason about the output of LLMs (though it is debatable how often will this be needed), you want the output from your imprecise human language spec to a deterministic spec (code) to be as easy to review as possible (for correctness, but mostly for any glaring errors). With proper setup, ensuring correctness of one deterministic output (Python) in comparison with another (eg. typed language like Rust) is just a deterministic run away (a test suite) that should not use any tokens from the LLM, and should have no practical differences in compute use.

1 comments

Static type checking catches a whole class of programming errors for free. Writing tests costs tokens or human time, so you end up needing more code (and probably more CPU time) to achieve the same level of error-checking in a dynamic language.
Very simplistic look, IMO. I'll add another one mostly as a counterpoint (not that I believe it is strictly true, but largely yes!).

Python is a lot more expressive than other languages and has a very terse syntax, and thus requires LLM to output much fewer tokens to achieve the same job compared to other languages.

Adding a few more tests to ensure data conformity on top of what you have to do anyway with a statically typed language still results in fewer tokens overall.

The expressiveness of a language and whether it does static/dynamic type checking are orthogonal. Type inference and generics are a thing in most modern languages. I also think you're underestimating the amount of code you need to guarantee that there are no type errors in your Python code.
You were talking about token economics: expressiveness surely matters there?
Does it have a terse syntax? I main F#, and when I have to work with Python I generally find myself complaining about how verbose it is. (Needing intermediate variables for what should have been a pipeline, the ceremony around parallelism, having to store constructor parameters as object fields, etc.)
It sure does in comparison with most mainstream statically typed languages — if you feel that way about Python, I wonder what you say about Java, C++ or even Rust or Go?

Checking some examples at https://learn.microsoft.com/en-us/dotnet/fsharp/tour, I'd say it's quite similar in verbosity — eg. no need to declare a module in Python since the code already lives in a file that is the module (plus one less indentation level for module level functions); inline function declaration and calling is thereabouts with F# slightly more terse (let vs lambda + spaces vs parenthesis); if-then more verbose in F# (no then in Python, just "if x:"); F# does not seem to need "return"...

In many cases you can avoid intermediate varibles: inline ifs, list comprehensions, lambdas, etc... Constructor arguments are a good point, but this is mostly about idiomatic use instead of language itself: you can simply do

  def __init__(self, **kwargs):
      self.args = kwargs
I'll give you one on the ceremony around concurrency, though! I have different ideas of how it should have been done to shift the cost to the language runtime instead of the developer, but alas... :)