Most active commenters
  • munificent(5)
  • svieira(3)

←back to thread

168 points todsacerdoti | 12 comments | | HN request time: 1.662s | source | bottom
1. munificent ◴[] No.41904629[source]
> I don’t think that JavaScript is inherently slow

It is.

Brilliant engineers have spent decades making it faster than you might expect, subject to many caveats, and after the JIT has had plenty of time to warm up, and if you're careful to write your code in such a way that it doesn't fall off the JITs optimization paths, etc.

Meanwhile, any typical statically typed language with a rudimentary ahead of time compiler will generally be faster than a JS VM will ever approach. And you don't have to wait for the JIT to warm up.

There are a lot of good things about dynamically typed languages, but if you're writing a large program that must startup quickly and where performance is critical, I think the right answer is a sound typed language.

replies(3): >>41905009 #>>41905395 #>>41905655 #
2. causal ◴[] No.41905009[source]
It mostly depends on the application. If you're doing complex transforms over hundreds of GBs of data- yeah, use a workhorse for that.

But the vast majority of slow JS I've encountered was slow because of an insane dependency tree or wildly inefficient call stacks. Faster languages cannot fix polynomial or above complexity issues.

replies(2): >>41905359 #>>41915860 #
3. chubot ◴[] No.41905359[source]
Yeah but the application we're talking about here is JavaScript tools, or more generally "language processors" / AST-based workloads

These are very different than your average JavaScript program

And that's exactly where it starts to be the case that JavaScript semantics are the issue

Take it from Lars Bak and Emery Berger (based on their actions, not just opinions): https://lobste.rs/s/ytjc8x/why_i_m_skeptical_rewriting_javas... :)

4. gagaq ◴[] No.41905395[source]
> I think the right answer is a sound typed language.

What do you mean by a "sound typed language". Go and Java have unsound type systems, and run circles around JS and Dart. Considering your involvement with Dart, I find contradictory information [1].

[1] - https://github.com/dart-lang/language/issues/1461

replies(2): >>41905691 #>>41907004 #
5. __s ◴[] No.41905655[source]
I spent years tuning a JS game engine to play nice with JIT for best performance. Then rewrote engine in Rust/WASM over a few weekends (turns out JIT friendly code is straightforward to port to statically typed language) & things are an order of magnitude faster now with the benefits of static type checking & no spooky jit perf to optimize for

Just because JS can be fast doesn't mean it's a pleasure to write fast JS

6. munificent ◴[] No.41907004[source]
> What do you mean by a "sound typed language".

I mean that if the type checker concludes than an expression or variable has type T, then no execution of the program will ever lead to a value not of type T being observed in that variable or expression.

In most languages today, this property it enforced with a combination of static and runtime checks. Mostly the former, but things like checked casts, runtime array covariance checks, etc. are common.

That in turn means that a compiler can safely rely on the type system to generate more efficient code.

Java intended to have a sound type system, but a hole or two have been found (which are fortunately caught at runtime by the VM). Go's type system is sound as far as I know. Dart's type system is sound and we certainly rely on that fact in the compiler.

There is no contradictory information as far as I know, but many people seem to falsely believe that soundness requires zero runtime checks, which isn't the case.

replies(1): >>41916937 #
7. munificent ◴[] No.41915860[source]
> If you're doing complex transforms over hundreds of GBs of data-

It's not always about giant datasets. Latency matters too for software that is run frequently by users.

I maintain a code formatter. In a typical invocation, it's processing only a few hundred kilobytes of input data. But it is invoked every time a user hits control-S, thousands of times a day. If I make it a few hundred milliseconds slower, it materially impacts their flow and productivity.

> Faster languages cannot fix polynomial or above complexity issues.

This is true. But once you have fixed all of your algorithmic issues, if your language is slow, you're completely stuck at that point. You have hit a performance ceiling.

Personally, I would rather work in a language where that ceiling is higher than it is in JavaScript.

8. svieira ◴[] No.41916937{3}[source]
> which are fortunately caught at runtime by the VM

At least one of them isn't, but that one is in a crufty old area of the code that most people don't care too much about:

https://blog.devgenius.io/java-106-why-does-sneakythrows-wor...

replies(1): >>41926169 #
9. munificent ◴[] No.41926169{4}[source]
Wow that's a new one for me. But as far as I can tell, that isn't violating soundness because I don't think Java uses checked exceptions as static types in the type system. All this means is that some methods might unwind with an exception type you wouldn't expect it to unwind from.
replies(1): >>41928297 #
10. svieira ◴[] No.41928297{5}[source]
Would you mind expanding on what you mean by "as static types"?

As I see it, it's definitely a part of Java's type system that things which throw a checked exception will require that you handle those checked exceptions. But now here comes `sneakyThrows` and the next thing you know your `Function#apply` (`R apply(I)`, not a `throws` to be seen in that declaration) is throwing a checked exception (not an `UndeclaredThrowableException` wrapping the checked exception, literally the checked exception ... unchecked).

But I'll admit I'm rather an amateur when it comes to such things.

replies(1): >>41929212 #
11. munificent ◴[] No.41929212{6}[source]
To violate soundness, you'd need to get into a situation where an unexpected checked exception leads an expression or variable to end up with a value of some type that doesn't match the expression or variable's static type.

This would be easy if the hole worked in the other direction where an exception should be thrown but isn't. Because then you could probably do something like confuse the definite assignment analysis in a constructor body to make it think all fields are initialized by the end of the constructor, like:

    class C {
      final int x;

      C(bool b) {
        if (b) {
          thingThatMustThrow();
        } else {
          x = 1;
        }
      }
    }
Here, if the control flow analysis expected that `thingThatMustThrow()` will always throw an exception, then it might conclude that all paths that reach the end of the constructor body will definitely initialize `x`. But if that method doesn't throw an exception, then execution could proceed merrily along and `x` never get initialized.

(In practice, this isn't an issue because field definite initialization analysis is already unsound so the VM default initializes every field anyway.)

But in this case, it goes in the other direction. You have code that isn't expected to throw that does. I can't think of a situation where that would lead Java to end up with a value of the wrong type flowing somewhere unexpected.

replies(1): >>41930527 #
12. svieira ◴[] No.41930527{7}[source]
Ah, "static" in the sense of "statically analyzable" because it's part of the typing phase. And "sound" here meaning only that the program can only assign values that match the statically determined types. Interesting that "soundness" doesn't include typing effects (at least in the literature), but I guess general typed effects are relatively new and we aren't yet at the point where Koka-like effect types are "expected functionality" when building a new programming language.

Thank you so much!