HNHacker News
TopNewBestAskShowJobs

mraleph

857 karma · joined September 29, 2010

mrale.ph
submissionscomments
mraleph··on Flutter 3.47
No, we (Dart team) recommend to deploy everything in AOT mode.

JIT mode is only for development, and its performance has not been a priority for a while now.

mraleph··on Elixir 1.19
We are done with NNBD transition for a few years now.

Dart 2.12 (released March 2021) introduced null-safety.

That started a 2 year transitionary period during which you could mix nullsafe (language versions 2.12 or above) and non-nullsafe (language versions below 2.12) code in one program.

Dart 3.0 (released May 2023) removed support for language versions prior to 2.12 - meaning that you can no longer opt out of null-safety.

mraleph··on A Walk with LuaJIT
At some point in my life (when I briefly worked on LuaJIT for DeepMind) I have written a stack walker which can stitch together native and Lua frames: for each native stack frame it checks if that is actually an interpreter frame or a trace frame - if that's the case it finds corresponding `lua_State` and unwinds corresponding Lua stack, then continues with native stack again.

This way you get a stack trace which contains all Lua and native frames. You can use it when profiling and you can use it to print hybrid stack traces when your binary crashes.

I was considering open-sourcing it, but it requires a bunch of patches in LJ internals so I gave up on that idea.

(There is also some amount of over-engineering involved, e.g. to compute unwinding information for interpreter code I run an abstract interpretation on its implementation and annotate interpreter code range with information on whether it is safe or unsafe to try unwinding at a specific pc inside the interpreter. I could have just done this by hand - but did not want to maintain it between LJ versions)

mraleph··on Ask HN: How is Kotlin and Compose multiplatform over flutter?
> - it is truly native, in the sense I can directly talk to native APIs on each platform without going through bridging code and there's no special runtime

I thought Compose Desktop is running on JVM an renders through Swing/AWT? Did this change?

mraleph··on Embedded Swift on the Raspberry Pi Pico
> The fact that I can’t then immediately go

Who said you can't? :) This actually works in Dart:

    Dog? dog;
    bool isDog = dog is Dog;
    
    if (isDog) {
      dog.bark();
    }
i.e. boolean variables which serve as witnesses for type judgements are integrated into promotion machinery.

I do agree with you that

1. In general there is no limit to developers' expectations with respect to promotion, but there is a limit to what compiler can reasonably achieve. At some point rules become too complex for developers to understand and rely upon. 2. Creating local variables when you need to refine the type is conceptually simpler and more explicit, both for language designers, language developers and language users.

I don't hate excessive typing, especially where it helps to keep things simple and readable - so I would not be against a language that does not do any control flow based variable type promotion. Alas many developers don't share this view of the world and are vehemently opposed to typing anything they believe compiler can already infer.

It's all a matter of personal taste in the end.

mraleph··on Embedded Swift on the Raspberry Pi Pico
The reason why languages promote variable types based on control flow is because developers en masse actually expect that to happen, e.g. facing the code like

    Dog? maybeDog;
    if (maybeDog != null) {
      maybeDog.bark();
    }
If compiler says "sorry, maybeDog might be null", developer (rightfully so) usually responds "but I have just checked and I know it is not, why do you bother me?". So languages chose to accommodate this.

> What if I want to set the value back to nil if it is not-nil?

You can. The type of the variable does not actually change. You could say that the information about more precise type is simply propagated to the uses which are guarded by the control flow. The following will compile just fine:

    Dog? maybeDog;
    if (maybeDog != null) {
      maybeDog.bark();
      maybeDog = null;
    }
> Why should I have to wrap things back in an optional if I want to pass it along as such?

You don't, with a few exceptions. There is a subtype relationship between T and T?: T <: T?, so the following is just fine:

    void foo(Dog? maybeDog);

    Dog? maybeDog;
    if (maybeDog != null) {
      maybeDog.bark();
      foo(maybeDog);  // Dog can be used where Dog? is expected
    }
 
You might need to account for it in places where type inference infers type which is too precise, e.g.

    Dog? maybeDog;
    if (maybeDog != null) {
      // This will be List<Dog> rather than List<Dog?>. 
      final listOfDogs = [maybeDog];
    }
Though I don't think it is that bad of a problem in practice.
mraleph··on Embedded Swift on the Raspberry Pi Pico
In Dart 3 you have patterns and sealed class families to achieve that. See my comment above[1].

The syntax is unfortunately more verbose, but if all goes well we will address that issue this year.

[1]: https://news.ycombinator.com/item?id=39613415

mraleph··on Embedded Swift on the Raspberry Pi Pico
In Dart 3 instead of declaring the variable and then comparing you can simply write an if-case pattern:

    if (value case final value?) {
      // value is a fresh final variable 
    }
mraleph··on Embedded Swift on the Raspberry Pi Pico
In Dart you use class hierarchies instead, rather than enums (which in Dart are way to define a compile time constant set of values). So the original example becomes:

    sealed class MySomething<T> {
    }

    final class One extends MySomething<Never> {
    }

    final class Two extends MySomething<Never> {
      final Child child; 
      
      Two(this.child);
    }

    final class Three<T> extends MySomething<T> {
      final T value;

      Three(this.value);
    }

    final class Four extends MySomething<Never> {
      final int Function() callback;

      Four(this.callback);
    }
And then you can exhaustively switch over values of MySomething:

    int foo(MySomething<String> foo) {
      return switch (foo) {
        One() => 1,
        Two(child: Child(:final value)) => value,
        Three(:final value) => int.parse(value),
        Four(:final callback) => callback(),
      };
    }
The declaration of a sealed family is considerably more verbose than Swift - and I really would like[1] us to optimized things for the case when people often declare such families. Macros and a primary constructors will likely provide reprieve from this verbosity.

But importantly it composes well with Dart's OOPy nature to achieve things which are not possible in Swift: you can mix and match pattern matching and classical virtual dispatch as you see fit. This, for example, means you can attach different virtual behavior to every member of the sealed class family.

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

mraleph··on Ask HN: Do you have a problem you'd pay to have taken away?
> Dart+Flutter look promising until you notice that they run Skia WASM inside the dart VM which itself runs in WASM which runs inside a JS VM and performance is abysmal.

That's not how Flutter works on either of the platforms it supports.

When you run natively (e.g. mobile, desktop, etc) Skia or Impeller execute natively and all your Dart code is compiled ahead-of-time to native code as well. No JS or Wasm is involved anywhere in the stack. No JIT compilation in release binaries, only during development.

When you run on the Web - then naturally Skia is compiled to Wasm and Dart code is compiled to JS or Wasm, JS VM will end up running both of those. No Dart VM is involved anywhere here.

mraleph··on Dart 3.1 and a retrospective on functional style programming in Dart
[disclaimer: I work on Dart, though not on the language team]

With primary constructors this will become something along the lines of:

    sealed class Message();

    class IncrementBy(final int value) extends Message;

    class DecrementBy(final int value) extends Message;

    class Set(final int value) extends Message;
Which is considerably less repetitive, though `extends Message` is still there. I am fairly optimistic that the next step would be to eliminate that[1], though I think we would need to gather a bit more feedback from users as people are getting more and more reps in with Dart 3.0 features. I personally would prefer something along the lines of:

    sealed class Message() {
      case IncrementBy(final int value);
      case DecrementBy(final int value);
      case Set(final int value);
    }
Current syntax is not all that bad if you are going to do OO and add various helper methods on `Message` and its subclasses, but if you just want to define your data and no behavior / helpers - then it is exceedingly verbose.

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

mraleph··on Will Carbon Replace C++?
> it was built on top of v8

Was never built on top of V8 in any shape of form. Dart VM does not even share any code with V8.

Dart 1 was designed by people who originally started V8 (Lars Bak and Kasper Lund), that's the only connection.

mraleph··on Google Trends for Zig array, Nim array, dart array, rust array, Past 12 months
Dart does not have arrays, it has lists. Try replacing "Dart array" with "Dart list" instead.
mraleph··on Building the fastest Lua interpreter automatically
It would be interesting to see 1-1 comparison with LuaJIT interpreter _without corresponding runtime changes_. That would given a meaningful baseline for how much you potentially loose/gain using this approach.

Current measurement presents comparison of two wildly different implementation strategies: LuaJIT Remake uses IC and hidden classes while LuaJIT proper does not. It's a well known fact that ICs+hidden classes can lead to major performance improvements. That insight originated in Smalltalk world and was brought to dynamic language mainstream by V8† - but unfortunately it muddles the comparison here.

† V8 was open sourced on September 2, 2008... Coincidentally (though probably not :)) JSC landed their first IC prototype on September 1, 2008.

mraleph··on The influence of Self
> The JIT still performs better, but the AOT code starts up faster.

I should update that side note because a lot of things has changed. It should probably say something like:

> Theoretically, Dart VM JIT should have best peak performance, while Dart VM AOT has best startup time. In reality the comparison is quite complicated. AOT has seen a lot of work in the last few years, which made it faster than JIT in some aspects e.g. both direct and highly polymorphic method calls are usually faster in AOT. JIT's peak performance has been languishing in a state of neglect, but it still runs circles around AOT in some cases due to aggressive inlining.

Something like this.

mraleph··on Amazon Corretto 18 is now generally available
The extension has recently reached Stage 2 - most of the questions around type system have been resolved. V8 has a working implementation. We have build `dart2wasm` compiler targeting this and it shows good numbers.
mraleph··on Amazon Corretto 18 is now generally available
> what's interesting is that static typing buys you somewhat minimal gains in performance.

There are a lot of caveats here. You could potentially claim "minimal gains in peak performance if you can apply adaptive JIT compilation techniques", but even that is stretching it somewhat.

Adaptive JITing comes at a price in terms of warmup, memory usage and implementation complexity. Fixed object shapes help somewhat to reduce the amount of checks needed but they don't take you all the way there. Optimising numerics remains challenging (e.g. think about optimising the case where a field always contains a `double` floating-point value or a field that always contains a 64-bit integer value). Knowing the shape of the container does not yield any information about the shape of elements which implies that some checks have to stay behind in the loops. Yes, monomorphic checks are usually simple (compare+branch) but polymorphic are not. And so on and so forth.

Yes, Dart 1 is easier to compile into efficient code compared to JavaScript. Dart 2 is even easier though - because it is more statically typed.

> but fear that it painted itself into a corner by first targeting memory managed languages.

FWIW WASM GC is coming - and it looks great.

mraleph··on Flutter 2
We are planning to fix it some time in the future, we really only expected moderate usage of `dart2native` and only on Linux. Turns out that there is demand for using it all over the place, which includes Mac and Windows and requires things like signing support... And to make executable signable we need to make them real Mach-O / PE executables and not weird concatenations of bytes.
mraleph··on Flutter 2
[I work on Dart VM team]

`dart2native` just concatenates two binaries together: AOT runtime binary and AOT snapshot binary. AOT snapshot is an ELF binary which contains native code generated from your original Dart code.

The approach is not pretty but was chosen as an implementation shortcut.

That's why `strip` does not actually do good things to the binary.

There is no reason to run `strip` on such binaries - because they don't contain anything strippable.

mraleph··on Graphical User Interface Using Flutter in Embedded Systems [pdf]
> 2. Dart has optional typing, which is to say it supports type hints but is essentially dynamically rather than statically typed.

Dart has not been optionally typed since Dart 2 (released in 2018).

mraleph··on Dart nullability syntax decision: a?[b] or a?.[b]
The operator itself would throw an error.

Dart is planning to have a sound non-nullability which means that if something has static type `SomeType` then it is guaranteed to be a value that is actually `SomeType` and not something else.

Thus you can't take `SomeType?` and just cast it to `SomeType` because `SomeType?` can be null and `SomeType` can't be null.

This means this cast has to perform a check and throw if your value is null.

This also means that if you have `e1` and `e1` has non-nullable static type then `e1` would never be null in the NNBD world - which is a very good property (e.g. for optimizations).

mraleph··on Dart nullability syntax decision: a?[b] or a?.[b]
It's a non-null assertion operator which would throw an error if `e1 == null`.
mraleph··on Announcing Dart 2.3: Optimized for building user interfaces
[disclaimer: I work on Dart team]

Short answer: there are a lot of different editors in the world, but Dart team is not large enough and does not posses necessary expertise to develop top-notch integration with all of them. So it makes sense to focus on the very popular ones (and both VSCode and IDEA are very popular) and provide very good support for those.

We hope that community can take and support other editors. That is why we provide tools (e.g. dart analyzer which has an LSP wrapper) that make it easy to build Dart plugins for any editor you like to use.

There are plugins for both emacs (https://github.com/bradyt/dart-mode) and vim (https://github.com/dart-lang/dart-vim-plugin), which I think work relatively well, but they are not maintained by the Dart team.

mraleph··on Flutter: a Portable UI Framework for Mobile, Web, Embedded, and Desktop
In the two experiments that we did LLVM brought only marginal benefits so we could not warrant the huge dependency and associated maintenance costs. We already have a good compilation pipeline, which was developed for the JIT mode and we use that for AOT with good results. Adding LLVM on top increases complexity - suddenly to tweak things you need to be an expert both in our compilation pipeline and LLVM (which is probably 100x larger than the whole Dart VM source code).

A lot of optimizations which benefit Dart code size and performance require high level optimizations anyway, so having LLVM does not help you in any way.

That said there are obvious benefits for having LLVM as a backend - so we are planning to explore it yet again in the near future.

mraleph··on Flutter: a Portable UI Framework for Mobile, Web, Embedded, and Desktop
DDC stands for Dart Development Compiler. It is a fast modular compiler that you use for quick edit&refresh development in the browser. It does not do any global optimizations, unlike dart2js - which is what you use for deployment.

If you write something like:

    import 'package:flutter_web/animations.dart';

    void main() => print('Hello, World!');
DDC would faithfully compile animations.dart as whole and ship that to your browser.

dart2js would include 0 bytes of code from animations.dart into the output.

> `webdev build` failed for me in examples/gallery

Maybe file a bug?

Here is what I get for Gallery:

    ╭─~/s/f/f/e/gallery ⟨master ⟩ ⟨9s449ms⟩
    ╰─» flutter packages pub global run webdev build
    ...
    Compiled 18,344,245 characters Dart to 1,914,077 characters JavaScript in 27.9 seconds
If I gzip the output I get around 500k.

Gallery uses a lot of Flutter so this is in some sense upper boundary for framework overhead.

Also it is still early days - I can clearly see this pushed down.

mraleph··on Flutter: a Portable UI Framework for Mobile, Web, Embedded, and Desktop
It's actually the very same main.dart.js (dart2js compiler does not have ability to target a specific browser, its output is supposed to work in every supported browser).

359kb is its compressed size, 990k is its uncompressed size.

Firefox shows compressed size in the "Transferred" column and uncompressed size in "Size" column.

In Chrome by default you see compressed size only, but if you click "Use large request rows" then you will see both compressed and uncompressed size.

mraleph··on Flutter: a Portable UI Framework for Mobile, Web, Embedded, and Desktop
There is some information at https://mrale.ph/dartvm/ but there is nothing really comprehensive right now.
mraleph··on Flutter: a Portable UI Framework for Mobile, Web, Embedded, and Desktop
> but it also compiles to ARM binary via LLVM for iOS applications

[disclaimer: I am TL for Dart Native Compilers]

We currently don't use LLVM for compiling Dart to native and we actually never used it in production - though we previously built couple of prototypes to evaluate potential benefits of using LLVM.

We use our own AOT compilation toolchain which has roots in our JIT compiler for Dart.

> meaning that compiling to WASM should be a trivial option

It's not really trivial because you still need to figure out some things - most importantly GC. On ARM you can scan the stack - on WASM you can't. They are working on GC support from WASM, but I don't think it is ready yet. And some things are just unfortunate (e.g. i31ref type which mimics V8's SMI - Dart SMI's are 63-bit on 64-bit platforms).

mraleph··on Flutter desktop shells
DBC is an interesting beast, the way it was implemented in Dart VM was not like a normal interpreter (e.g. you interpret and then you tier up into JIT which generates native code), but kinda like a pseudoarchitecture. DBC tiers up into... itself. An optimizing JIT in DBC mode would generate optimized DBC bytecodes, not native code. This all kinda makes sense in the environment for which DBC was created - iOS, where you can't JIT. However in environment where you can generate native code this makes no sense - and even prevents you from reaching peak performance.

KBC is an experiment in this space where you have a more classical interpreter which tiers up into native JIT. In KBC mode bytecodes are also given to VM rather than generated internally inside the VM like DBC does.

Theoretically KBC should have better startup latency than just JIT (because you can start executing code immediately, rather than spending time to generate native code) and peak performance that matches JITs - because it tiers up into JIT.

mraleph··on Flutter desktop shells
Dart was actually really opinionated right from the start. Type annotations that has no bearing on execution; reified generics and dynamic typing at the same time; mirrors and noSuchMethod... even having semicolon - as opposed to not having it. These are all strong opinions about language design. They might not align with what is considered "modern" these days, but they are all opinions nevertheless.
Page 1 of 10Next →