HNHacker News
TopNewBestAskShowJobs

sjrd

1,276 karma · joined June 15, 2013

Compiler Engineer, Scala Center, EPFL. I develop Scala.js https://www.scala-js.org/
submissionscomments
sjrd··on A new faster algorithm to compute 64-bit signed floor division
This looks very interesting, and very well written. I'll definitely give a it a thorough read when I get the time, and probably include the technique in our compiler. Floor division is almost always more useful than truncating division, so having a fast path there would be nice.

I can't help but do a shameless plug for another recent result in that space: we developed fast algorithms for 64-bit integer divisions (by constants and non-constants) for JavaScript, where you don't even have 64-bit integers to start with. Paper [1] and slides [2] available. The Scala.js compiler uses all of that. The Kotlin/JS compiler recently accepted my PR for the non-constant divisors algorithms.

[1] https://arith2026.org/papers/64-bit%20Integer%20Division%20f...

[2] https://arith2026.org/slides/session_1_2_doeraene.pdf

sjrd··on Music Theory for Programmers
Perfect pitch is not correlated with the ability to see written notes and immediately know how to play them on any instrument. It is only barely correlated with the same but for singing the note.

I do not have perfect pitch, but I could always do what GP's kid does. I am now a pretty good amateur singer.

What matters for this is learning to read the music notation and developing a sense (mostly subconscious) of scales and harmony. Perfect pitch does not help either of those things.

sjrd··on New type of dice guarantees no tie when deciding who goes first
Better source: http://www.ericharshbarger.org/dice/go_first_dice.html

TFA claims it's "new" in 2026, but the current state of the art seems to still be that of 2022.

I bought actual dice like these in 2024 from https://mathsgear.co.uk/collections/dice/products/go-first-d...

So well, is TFA just a big pile of slop?

sjrd··on Moving integer division to floating-point is trivial
There is a paper by Vincent Lefèvre that indeed proves that floating point division and a floor implement the euclidean division, with a careful analysis of when. [1] A corollary of the main theorem in the paper is that, with round to nearest, if x and y fit in 53 bits unsigned then it works out.

[1] https://hal.univ-lorraine.fr/inria-00070403v1

sjrd··on Since Chromium 148, Math.tanh is now fingerprintable to link underlying OS
I guess that's one more good reason to push for correctly rounded transcendental functions. I recently learned that they're basically solved now. [1]

[1] https://arith2026.org/program.html (2nd keynote)

sjrd··on Lumina – a statically typed web-native language for JavaScript and WASM
Have you tried Scala? It checks all the boxes, and is a mature language. The reactive UI runtime is provided by the library Laminar [1].

Technically its type inference is not HM but it's as expressive. In particular it has GADTs and HKTs, which I saw in your docs.

I wonder what you feel is missing from Scala (its .js/Wasm version) that Lumina provides?

[1] https://laminar.dev/

sjrd··on Someone bought 30 WordPress plugins and planted a backdoor in all of them
Try Scala? You only need one 0-dependency library for UI (Laminar), and you're good to go.
sjrd··on Most people can't juggle one ball
Oh waw, I had totally forgotten about the handkerchiefs. But this is indeed how I was first thaught juggling when I was a kid. Thanks for the trip down the memory lane!
sjrd··on JSIR: A High-Level IR for JavaScript
Right. I believe we are in agreement on that.
sjrd··on JSIR: A High-Level IR for JavaScript
Only compilers that already had an SSA-based pipeline transform SSA to stack-based for Wasm. And several don't like that they have to comply with Wasm structured control flow (which, granted, is independent from SSA). Compilers that have been using an expression-based IR directly compile to Wasm without using an SSA intermediary.
sjrd··on JSIR: A High-Level IR for JavaScript
It seems to me that there's a certain "blindness" between two compiler worlds.

Compiler engineers for mostly linear-memory languages tend to only think in terms of SSA, and assume it's the only reasonable way to perform optimizations. That transpires in this particular article: the only difference between an AST and what they call IR is that the latter is SSA-based. So it's like for them something that's not SSA is not a "serious" data structure in which you can perform optimizations, i.e., it can't be an IR.

On the other side, you actually have a bunch of languages, typically GC-based for some reason, whose compilers use expression-based structures. Either in the form of an AST or stack-based IR. These compilers don't lack any optimization opportunities compared to SSA-based ones. However it often happens that compiler authors for those (I am one of them) don't always realize all the optimization set that SSA compilers do, although they could very well be applied in their AST/stack-based IR as well.

sjrd··on Show HN: The Mog Programming Language
Hum, IIRC, using your definition of an AOT compiler, then V8 is an AOT compiler. V8 never interprets code. It immediately compiles it to machine code. It improves it later, but it's never slow.
sjrd··on Show HN: The Mog Programming Language
Wasm is definitely designed to be compiled, either ahead of time or JITed. Wasm interpreters are few and far between.
sjrd··on Bitwise conversion of doubles using only FP multiplication and addition (2020)
Thanks for the feedback, and congrats on your achievement.

We do still maintain this code, although it is deprecated now.

Even with the unrolled tests, we would still keep the table for the decoding operation, I believe. But it's true that it would at the same time provide the normalized value. That could be beneficial.

sjrd··on Bitwise conversion of doubles using only FP multiplication and addition (2020)
Interesting. When compiling Scala.js to ECMAScript 5, we still have an implementation of bitwise floating point conversions based on double operations and integer shifts. [1] We also use a lookup table for powers of 2, and don't use anything but primitives (no log or pow, notably). We do have a few divisions, but I see now how I could turn them into multiplications. Dealing with subnormals is tricky because the inverse of the subnormal powers of 2 are not representable.

We have one loop: a binary search in the table of powers of 2 for the double-to-bits conversion. It has a fixed number of iterations. I had tried to unroll it, but that did not perform any better.

I'll have to dig more to understand how they got rid of the comparisons, though.

I wonder whether their implementation would be faster than ours. At the time I wrote our conversions, they beat every previous implementation I was aware of hands down.

[1] https://github.com/scala-js/scala-js/blob/v1.20.2/linker-pri...

sjrd··on Stop Doom Scrolling, Start Doom Coding: Build via the terminal from your phone
I genuinely did that a few times. Using an ssh client to fix a commit failing CI, for example. Even launching release builds remotely. Notably once when I was on vacation and half the Scala ecosystem was waiting for me.
sjrd··on DEC64: Decimal Floating Point (2020)
JavaScript engines do optimize integers. They usually represent integers up to +-2^30 as integers and apply integer operations to them. But of course that's not observable.
sjrd··on D2: Diagram Scripting Language
In Scala you can do it, because you can define your own operators (which are nothing but method names), and you can extend types you don't control. You are a bit constrained by the operator precedence rules, but it's usually good enough.

It's bad practice to make DSLs left and right, obviously. But when one is warranted, you can.

For example here you could have

    "x" --> "y" | "hello world"
sjrd··on A years-long Turkish alphabet bug in the Kotlin compiler
I guess it's one way to look at it. I see it as: I want a reproducible locale, independent of the user's system. If I see US, I'm wondering if it was chosen to be English because the program was written in English. When I localize the program, should I make that locale configurable? ROOT communicates that it must not be configurable, and never dependent on the system.
sjrd··on A years-long Turkish alphabet bug in the Kotlin compiler
I am one of the maintainers is the Scala compiler, and this is one of the things that immediately jump to me when I review code that contains any casing operation. Always explicitly specify the locale. However, unlike TFA and other comments, I don't suggest `Locale.US`. That's a little too US-centric. The canonical locale is in fact `Locale.ROOT`. Granted, in practice it's equivalent, but I find it a little bit more sensible.

Also, this is the last remaining major system-dependent default in Java. They made strict floating point the default in 17; UTF-8 the default encoding some versions later (21?); only the locale remains. I hope they make ROOT the default in an upcoming version.

FWIW, in the Scala.js implementation, we've been using UTF-8 and ROOT as the defaults forever.

sjrd··on Rating 26 years of Java changes
Or Scala. Or Kotlin. Or any of the other languages that had most of these features years if not decades before Java. ;)
sjrd··on The Temporal Dead Zone, or why the TypeScript codebase is full of var statements
I'm sure it can do that in many cases. But if the scopes are a bit complicated, and in particular when variables are captured in lambdas, it's just not possible. The semantics require the TDZ behavior. If you can statically analyze that the TDZ won't be triggered, you can lower to `var`, but otherwise you have to keep the checks.
sjrd··on The Temporal Dead Zone, or why the TypeScript codebase is full of var statements
Usage of Scala.js is steadily growing. Several indicators suggest that 1 in 5 Scala developers use Scala.js at this point. It's regularly brought up as one of the strongest suits of Scala.

Usage of Scala itself is less shiny if you look at market share. But I believe it's still growing in absolute numbers, only quite slowly.

sjrd··on The Temporal Dead Zone, or why the TypeScript codebase is full of var statements
Indeed, `let`s and `const`s incur a significant performance penalty. This is also why the Scala.js compiler emits `var`s by default, even when targeting very recent versions of ECMAScript.

The good news is that we can still write our Scala `val`s and `var`s (`const` and `let`) in the source code, enjoying good scoping and good performance.

sjrd··on Why do some gamers invert their controls?
I'm not surprised. I have introduced several people to gaming, both adults and children. I let them all start with the default settings, and I don't even tell them there are settings. Then I observe their movements. I observe whether they consistently (or very often) start looking the wrong way before correcting. If they do that a lot, I change the settings, and it's smooth sailing from there.

So from my anecdotal perspective, explanations based on previous experience make no sense. It had to be something more innate, more related to how our brains are "wired".

Some people invert Y but not X. This is the most surprising to me. Most I've seen invert both. I don't remember having seen someone invert X but not Y.

Personally I invert both, except for games with a mouse to aim (like 3rd person shooters). In that case I invert neither. Go figure.

sjrd··on WASM 3.0 Completed
If your language and its compiler use JS String Builtins (part of Wasm 3.0) for their strings, then there is no cost to give them to JS and the DOM.
sjrd··on WASM 3.0 Completed
Wasm 3.0, with its GC and exception support, contains everything you need. The rest is up to the source language to deal with. For example, in Scala.js [1], which is mentioned in the article, you can use the full extent of JavaScript interop to call DOM methods right from inside your Scala code. The compiler does the rest, transparently bridging what needs to be.

[1] https://www.scala-js.org/doc/project/webassembly.html

sjrd··on WASM 3.0 Completed
Binaryen has a lot of baggage from Wasm early days, when it was still a strict AST. Many newer features are difficult to manipulate in its model.

In our compiler (featured in TFA), we chose to define our own data structure for an abstract representation of Wasm. We then wrote two emitters: one to .wasm (the default, for speed), and one to .wat (to debug our compiler when we get it wrong). It was pretty straightforward, so I think the instruction set is quite nice. [1]

[1] https://github.com/scala-js/scala-js/tree/main/linker/shared...

sjrd··on We shouldn't have needed lockfiles
Having read the article and read some of the comments here, I think many could learn from dependency management rules in the Scala ecosystem.

Scala uses Maven repositories (where the common practice is to use fixed dependency versions) but with different resolution rules:

* When there are conflicting transitive versions, the highest number prevails (not the closest to the root).

* Artifacts declare the versioning scheme they use (SemVer is common, but there are others)

* When resolving a conflict, the resolution checks whether the chosen version is compatible with the evicted version according to the declared version scheme. If incompatible, an error is reported.

* You can manually override a transitive resolution and bypass the error if you need to.

The above has all the advantages of all the approaches advocated for here:

* Deterministic, time-independent resolution.

* No need for lock files.

* No silent eviction of a version in favor of an incompatible one.

* For compatible evictions, everything works out of the box.

* Security update in a transitive dependency? No problem, declare a dependency on the new version. (We have bots that even automatically send PRs for this.)

* Conflicting dependencies, but you know what you're doing? No problem, force an override.

sjrd··on LLMs as Compilers
The level of abstraction of programming languages has been growing, yes. However, new languages have preserved precision and predictability. I would even argue that as we went up the abstraction ladder, we have increasingly improved the precision of the semantics of our languages. LLMs don't do that at all. They completely destroy determinism as a core design. Because of that, I really don't think LLMs will be the future of programming languages.
Page 1 of 10Next →