Live data from Hacker News

Why static languages suffer from complexity

hirrolot.github.io

291–300 of 306 posts

Re: Why static languages suffer from complexity

#291

Earlier quoted context omitted.

Heh, I would actually consider automatic transmission to be the more expressive one, since to me expressive means how easy it is to express something. Analogously e.g. C++ (manual) is more efficient and allows finer control, but makes it harder to express the same thing as in a 'higher level' (automatic) language. Otherwise, since Assembly provides the most control out of all, would you consider it the most expressiv…

I guess in my head "expressiveness" is some fuzzy combination of what you are able to do plus how easy it is to do those things. I'd consider a calculator that supports real numbers to be more expressive than one which only supports integers, all else being equal. Maybe this definition is idiosyncratic, though. It's certainly not objective.

I'd agree that "what you are able to do" seems like it's intuitively part of expressiveness, but due to Turing completeness you don't have any situation where language A can compute something that language B can't. So the only difference in capabilities seems to be in how easy it is to compute something, rather than if one is able to do something.

Re: Why static languages suffer from complexity

#292
post #107

Earlier quoted context omitted.

No - (a) is not what I'm suggesting. And (b) while disappointing, just doesn't slow one's work down very frequently in daily practice. Look, I just don't buy the suggestion that static typing magically solves a huge set of problems (or that it does so without imposing negative tradeoffs of its own -- the very topic of the original article). Or that dynamic languages are plainly crippled, and that one has to be a kind…

You suggested that Python type hints are useful. I laughed hard at that suggestion. Can you maybe just show how to type a Python function such that it does the absurdly simple thing of taking a numpy array of integers? def fun(nump_array_of_ints: ???): ... Just to show everyone just how "useful" type hints in Python _actually_ are.

  import numpy as np
  import numpy.typing as npt

  x = np.array([1, 2, 3])
  def foo(x: npt.NDArray[np.int_]) -> bool:
      return True

Re: Why static languages suffer from complexity

#293
post #284
post #280

Earlier quoted context omitted.

So why can't Nim infer from let b: uint = a that you're really just saying let b: uint = uint(a) And BTW don't you get tired of typing (and reading) `uint` twice in the latter setting? That's what I mean about "side effects" after all.

> So why can't Nim infer from `let b: uint = a` It "can", but it's a design decision not to by default because mixing `uint` and `int` is usually a bad idea. This is telling the compiler you want to add an `int` that represents (say) 63 bits of data with a +/- sign bit to a `uint` that doesn't have a sign bit. If `a = -1` then `b = uint(a)` leaves `b == 18446744073709551615`. Is that expected? Is it a bad idea? Yes.…

Okay, int/uint was a bad example; but what about

  let a: int = 1
  let b: float = a
Why wouldn't we want our dream language to infer a coercion here?

That said, Python's behavior (though correct to spec) is arguably worse:

   a: int = 1
   b: float = a 
   print(b, type(b))
   >>> 1 
With no complaints from mypy.

Re: Why static languages suffer from complexity

#294
post #293
post #284

Earlier quoted context omitted.

> So why can't Nim infer from `let b: uint = a` It "can", but it's a design decision not to by default because mixing `uint` and `int` is usually a bad idea. This is telling the compiler you want to add an `int` that represents (say) 63 bits of data with a +/- sign bit to a `uint` that doesn't have a sign bit. If `a = -1` then `b = uint(a)` leaves `b == 18446744073709551615`. Is that expected? Is it a bad idea? Yes.…

Okay, int/uint was a bad example; but what about let a: int = 1 let b: float = a Why wouldn't we want our dream language to infer a coercion here? That said, Python's behavior (though correct to spec) is arguably worse: a: int = 1 b: float = a print(b, type(b)) >>> 1 With no complaints from mypy.

We don't want to automatically convert between `int` and `float` because there's a loss of information, since floats aren't able to represent integers precisely.

However, we don't need to specify types until the point of conversion:

    let a = 1
    let b = a.float
> Python's behavior (though correct to spec) is arguably worse

Yeah that is not ideal. Looking at the code it seems logical at first glance to expect that `b` would be a `float`. In this case, the type hints are deceptive. Still, it's not as bad as JavaScript which doesn't even have an integer type! Just in case you haven't seen this classic: https://www.destroyallsoftware.com/talks/wat

Another gotcha I hit in Python is the scoping of for loops, e.g.,https://stackoverflow.com/questions/3611760/scoping-in-pytho...

Python takes a very non-obvious position on this from my perspective.

Ultimately, all these things are about the balance of correctness versus productivity.

I don't want to be writing types everywhere when it's "obvious" to me what's going on, yet I want my idea of obvious confirmed by the language. At the other end of the scale I don't want to have to annotate the lifetime of every bit of memory to formally prove some single use script. The vast majority of the time a GC is fine, but there are times I want to manually manage things without it being a huge burden.

Python makes a few choices that seem to be good for productivity but end up making things more complicated as projects grow. For me, being able to redefine variables in the same scope is an example of ease of use at the cost of clarity. Another is having to be careful of not only what you import, but the order you import, as rather than raise an ambiguity error the language just silently overwrites function definitions.

Having said that, as you mention, good development practices defend against these issues. It's not a bad language. Personally, after many years of experience with Nim I can't really think of any technical reason to use Python when I get the same immediate productivity combined with a static type checking and the same performance as Rust and C++ (also no GIL). Plus the language can output to C, C++, ObjC and JavaScript so not only can I use libraries in those languages directly, and use the same language for frontend and backend, but (excluding JS) I get small, self contained executables that are easily distributable - another unfortunate pain point with Python.

For everything else, I can directly use Python from Nim and visa versa with Nimpy: https://github.com/yglukhov/nimpy. This is particularly useful if you have some slow Python code bottlenecking production, since the similar syntax makes it relatively straightforward to port over and use the resultant compiled executable within the larger Python code base.

Perhaps ironically, as it stands the most compelling reason not use Nim isn't technical: it's that it's not a well known language yet so it can be a hard sell to employers who want a) to hire developers with experience from a large pool, and b) want to know that a language is well supported and tested. Luckily, it's fairly quick to onboard people thanks to the familiar syntax, and the multiple compile targets make it able to utilise the C/C++/Python ecosystems natively. Arguably the smaller community means companies can have more influence and steer language development. Still this is, in my experience, a not insignificant issue, at least for the time being.

Re: Why static languages suffer from complexity

#295
post #269
post #215

Earlier quoted context omitted.

> What if the JSON represents a list, or an int? Then you write one short operator (and I agree that some static languages make this more cumbersome than it should be) to say so, and either handle the case where it isn't, or explicitly declare yourself partial and not handling it. > Also, how do you then access nested objects, like data['key'][0]['attr'] in Python? With lenses, something like: data ^? (key "key") >>>…

Sure there are solutions. But my main point is that HideousKojima's "statically-typed" solution would result in a runtime type error if it was given unexpected input, just like a dynamically typed solution.

> But my main point is that HideousKojima's "statically-typed" solution would result in a runtime type error if it was given unexpected input, just like a dynamically typed solution.

I don't think HideousKojima ever called it a "statically-typed solution". Their point was that statically-typed languages still let you write unchecked code when you want to - and yes, of course such unchecked code can fail at runtime - but give you the option of having checking in the cases where you want it.

Re: Why static languages suffer from complexity

#296

Earlier quoted context omitted.

> just like you have to evolve your specification/documentation. That is correct, and also one of the core reasons why in the vast majority of cases either no specification/documentation exists, or will only cover a small case of the actual specification. For example I would bet money that not a single function in the C, C++, Java and Python standard libraries is fully specified, in the sense of nailing down the prog…

>the core reasons why in the vast majority of cases either no specification/documentation exists I feel that is much too pessimistic. >will only cover a small case of the actual specification. If the same applies to proofs: so be it. Don't let perfect be the enemy of good! >For example I would bet money that not a single function in the C, C++, Java and Python standard libraries is fully specified, in the sense of na…

> Take the spec for all sorting algorithms (giving observational equivalence):

It's not that simple. You also have to specify the effect set of the algorithm, meaning, assuming we do in place sort: every memory cell other that the input array are unchanged after termination. (If you return a fresh array, you have to specify something related). You also have to specify what happens for general sorting predicates, for example if the sorting predicate prints out the element it compares then this reveals (much of) the algorithm.

> The compression is stripping away the 'how'

The sorting example shows that this largely doesn't work in practise. In my experience for non-trivial programs you tend to have to carry around invariants whose complexity matches the size of the program you are verifying.

> I'm convinced that larger scale proof automation is way more essential than HoTT.

I strongly agree, but currently this proof automation is largely a hope, rather than a reality.

> crazy type hackery as seen in Haskell or Scala

Haskell and Scala have (local) type-inference. That makes those complex types (somewhat) digestible.

> dependent types just for refinement

If / when this really works well, so that you don't pay a price when you are not using complex types, then this would be very nice. I don't think PL technology is there yet. (I'm currently using a refinement typing approach in production code.)

Re: Why static languages suffer from complexity

#297
post #273

Earlier quoted context omitted.

It is probably pretty presumptuous to assume, but I think that a lot of programmers that have only every been exposed to C/C++/C#, Java and Python have basically no concept of what a good type system can do for them. Two examples from the top of my head: 1. Encoding matrix sizes into the data- and function-types, so that you can safely have a function `mat[c,b] mat_mult(mat[a,b] a, mat[c,d] b)` or even `mat[w-2,h-2]…

For example one, it works for signal processing or graphics but compile-time dimensions are unusable in Machine Learning or Numerical Computing because it's too much friction on serialization/deserialization and some operations that reduce dimension or rank are based on runtime data (for example some dimensions are 1)

Sure, but this approach generalizes quite well. Especially in ML you have have a lot of matrices, many of them of known size (e.g. convolution kernels).

Also, while it looks like the matrix sizes have to be known at compile time, this is not the case. You are still free to use the same matrix types with dynamic sizes (or even mix them, useful for said convolutions).

In Haskell there that is achieved by using a "KnownNat", basically you elevate an integer from the value into the type level during run-time.

Re: Why static languages suffer from complexity

#298
post #271

Earlier quoted context omitted.

I can see this kinda. It would be interesting to experiment with how black and white this is. Historically, most cases have been either compile time (static) or run time (dynamic) type checking. And left between one or the other, and experiences like the above, people make their binary choice. More and more in my Python code, I do some type annotations I can. My feeling is that the annotation coverage ROI is non line…

> either compile time (static) or run time (dynamic) type checking But it is not that black and white, is it? Python is actually somewhat static in that it checks (some) types during runtime. Other dynamically typed languages live completely by the "when it quacks like a duck" playbook. On the other hand, Haskell is completely statically typed. Still you can write many programs without annotating any types at all, as…

[deleted]

Re: Why static languages suffer from complexity

#299

Earlier quoted context omitted.

In addition to what others have said about just passing two parameters, there also row types, where the signature of `calculate_price` can be specified to accept any record that has the two required fields.

Isn't that duck type?

I don't write Python, but I think row level typing is stricter. Both the names and types of the record fields would have to satisfy the function signature, so the quacking is only honored on field names. Where dynamic languages will of course accept floats where ints are called for, etc, quacking all the way down.

The point of my original comment was to suggest that some of the flexibility offered by duck typing can be achieved in FP, so they should seem similar.

I would still just pass the fields as two parameters.

Re: Why static languages suffer from complexity

#300

Almost all software running the world is written in statically typed languages. This is not by accident or because developers don’t know better. Every few months on HN somebody will make some new claim about why dynamically typed languages are somehow better. But the truth is that statically typed languages have won in the market place for real world software. And I don’t see anything changing that.

Today I learned that python, javascript and php are statically typed languages.

Python, JavaScript and PHP run on runtimes written in statically typed languages. And those runtimes run on operating systems written in statically typed languages, using hardware drivers written in statically typed languages. So yes the world does indeed run on statically typed languages. The code you write in Python/JavaScript/PHP is a thin layer on top of C/C++.
Post reply on HN