I'm on the Dart team (though I wouldn't necessarily take my comment to be an official statement of the entire team).
> (no non-nullable types)
I really wanted [1] to get those into Dart 1 (way back before Swift and TypeScript even existed), but I couldn't convince language team at the time that it was worthwhile.
When we moved to a stricter, sound type system with strong mode, we hoped to get non-nullable types into that and ship them with Dart 2. But, as you can imagine, migrating millions of lines of code from an optionally typed, unsound type system to a sound, reified static type system is a hell of a lot of work. (I'm not aware of any cases where it's been done at this scale.)
We weren't able to fit non-nullable types into that schedule and into our users' migration pain tolerance. There is only so much you can drag them through, and just getting to strong mode was a lot.
There is still a desire to bring non-nullable types to Dart. It probably won't be soon because we want to give our users a break from migration, and give our implementation teams time to take advantage of the new type system. But I haven't given up on them, and our team's new focus on static ahead-of-time compilation makes them more important than ever.
I agree that Kotlin is a really nice language. I hope we can catch up to them with Dart and exceed them in areas.
The thing is that the longer you wait, the harder it will be to migrate, as the codebase size grows (and once you leave beta, people assume that the language is finalized and won't be too happy being forced to refactor their code when Dart 3 comes out). But as it is, I doubt that the nice parts of Kotlin will ever make it to Dart.
> The thing is that the longer you wait, the harder it will be to migrate.
Definitely preaching to the choir on that one. I pressed my case as hard as I could before 1.0.
We do still have more freedom to make breaking changes than many post 1.0 languages do because, frankly, we don't have that many users. But every day, the cost to make that change goes up.
> people assume that the language is finalized and won't be too happy being forced to refactor their code when Dart 3 comes out
I was worried about that with the transition to the new type system in Dart 2, but users — internal and external — were surprisingly accepting of the breakage. We don't want to be cavalier about breaking them, of course, but my impression is that there is more room for significant changes than I'd initially assumed.
> But as it is, I doubt that the nice parts of Kotlin will ever make it to Dart.
Dart will never be Kotlin, but I hope we can get to a point where most users don't consider it to be deficient compared to Kotlin and where we have some features to make Kotlin users jealous.
It's because the language was made to compile to JS, and competition to JS, so appropriate tradeoffs were made.
For example:
1. JS is single-threaded, with extremely heavy threads only recently made available. So Dart is single threaded, with extremely heavy threads available.
2. JS is weak-typed, so Dart was made optional typed. Remember, it was made before typescript, so they probably didn't expect that such type-heavy features as ADT would interest people.
Kotlin does the same (for example, internal immutability would be much nicer, but since the JVM doesn't support that, neither does Kotlin).
1. They added optional nullability syntax to objective-c, a language that assumes everything is null and doesn't crash when you send a message to nil. If your file uses nullability, it's required for the file, otherwise you don't have to use it. Code without nullability annotations using non-null annotated code could interface with the non-nullable code. Errors didn't come up unless you did very obvious things like directly put nil in a non-null argument.
2. They migrated their entire apple library base to have proper nullability annotations. Code that didn't have nullability annotations continued to work fine. Or I think you could just turn off the build error with a compiler flag.
3. Apple introduced swift. Any objective-c code that didn't have nullability defined was assumed to be an implicitly unwrapped non-null. So if you give a null to something not expecting a nil in swift, it would crash.
I liked this approach for the most part. Migration wasn't that much of a pain, and it was incentivized because I didn't want implictly unwrapped stuff in my swift code.
If you migrated the flutter framework and the dart stdlib to have nullability, then new projects can start with proper nullability annotations from the start, while old projects can progressively migrate files as needed.
Java also has a nullable syntax annotation, and kotlin probably has some sort of java-kotlin nullability interop somewhere. I would really suggest doing it, nullability has given big stability benefits to large projects as it is, and would help adoption in the future as people using something like kotlin & swift for new projects vs flutter itself.
not sure what's dart like relative to enums, but enum with associated type + optionnals are the absolute killer feature for swift. Steal those and you'll have nothing to worry about.
As far as I know, both Hack and Flow are unsound and don't do any runtime type checks to preserve soundness. It's a lot easier to migrate dynamic code to a static type system if you have the luxury of just ignoring the type system when you want to. :)
In Dart 2, the type system is sound and checked at runtime in cases where it can be proven statically safe (downcasts, variance, etc.). That makes it a lot more work to migrate because the code actually needs to run correctly without violating any of the dynamic type tests.
Neither Hack nor Flow are reified, so it's a different matter.
Both of these languages started out with strict null-checking — Hack because there were too many bizarre falsy values in PHP to allow otherwise, and Flow because it worked out so well in Hack. So neither had to tack on strict null-checking afterwards.
There have been some similar large-scale migration efforts. For example, Hack's record types were built on top of PHP arrays, which don't distinguish between absent and null values. Consequently, Hack's record types didn't distinguish between optional and nullable fields. Furthermore, records support width subtyping by default. This is unsound: you can construct distinct types A and B with A <: B <: A.
After a lot of effort, we did migrate the entire codebase to resolve this unsoundness. Essentially, we changed every record declaration to be an "open" record type and made all new records "closed" by default (referring to whether they supported width subtyping).
The analogue, then, would be something like refactoring every type declaration in every consumer's Dart code to be annotated as nullable, adding explicit null-checks, and allowing them to write non-nullable types as the default thenceforth.
The story of how I ended up on the team is probably too random to be actionable. I believe we do have open headcount, but I don't know how the hiring process works for getting a new hire on to a particular team.
Sad to see optional typing go. I thought that's the best of both worlds -- you can write type annotations for documentation or checking, but you also have the benefit of dynamic typing when static typing comes in the way.
> (no non-nullable types)
I really wanted [1] to get those into Dart 1 (way back before Swift and TypeScript even existed), but I couldn't convince language team at the time that it was worthwhile.
When we moved to a stricter, sound type system with strong mode, we hoped to get non-nullable types into that and ship them with Dart 2. But, as you can imagine, migrating millions of lines of code from an optionally typed, unsound type system to a sound, reified static type system is a hell of a lot of work. (I'm not aware of any cases where it's been done at this scale.)
We weren't able to fit non-nullable types into that schedule and into our users' migration pain tolerance. There is only so much you can drag them through, and just getting to strong mode was a lot.
There is still a desire to bring non-nullable types to Dart. It probably won't be soon because we want to give our users a break from migration, and give our implementation teams time to take advantage of the new type system. But I haven't given up on them, and our team's new focus on static ahead-of-time compilation makes them more important than ever.
I agree that Kotlin is a really nice language. I hope we can catch up to them with Dart and exceed them in areas.
[1]: http://journal.stuffwithstuff.com/2011/10/29/a-proposal-for-...