I'm not sure I ultimately buy that Dropbox is expensive - for one thing, Notion now charges basically the same as Dropbox, and you don't even get file sync in the deal.
I can definitely believe that that was an objection customers had, though. I just suspect that what it really meant was another way of expressing that there wasn't PMF - if the PMF were there, they'd have willingly paid, just like they do now for Notion or Slab or what have you.
And yeah, there was definitely some energy going into trying to tie the products together more (putting Paper docs in your Dropbox folders) - and when that finally shipped it sadly made the Paper experience worse, not better.
Yeah, when signing into Paper it always felt pretty silly how the auth flow was all like "are you sure you want to share your Dropbox account info with this Paper thing?" as if it was some third-party service.
Ironically, just within the last year Paper has gotten much more integrated into Dropbox as a single UX. And… it's significantly worse: slower, clumsier, harder to navigate. (I don't think there's any inherent reason those had to be correlated; it's just that Paper has clearly been destaffed a lot in recent years, so naturally any new changes will tend to be less polished.)
And they had Paper, which was an excellent product (I was at Dropbox a decade ago; we all used Paper constantly and it was great) very close to what Notion later became. They never got it over the hump to wider PMF — like you say, a failure of product and of enterprise execution.
(Given that it was so close to Notion, I think Paper is one area where the product vision was on to something good; but they didn't succeed at product execution, connecting customer feedback to iterating correctly on product improvements.)
Eich's own company, Brave, is pushing AI plenty hard: the Brave browser promotes a "smart AI assistant" called Leo. That's much more AI integration than I see in Firefox.
Thanks for sharing that story! I love that your dream was to be the last line of defense protecting users from bad software. We need more of that, and it's sad that execs at Microsoft and others have made it harder.
Definitely some blame also belongs to the Xorg committer who reviewed and merged those changes (and it looks like that person understands that in retrospect). But the primary responsibility for getting a change right is the author's.
I haven’t seen a lot of discussion here of other existing apps moving to Flutter, which surprises me because my experience and my team’s has been so good — the performance, the quality of the code and documentation, and also our experience working with upstream. So I thought I’d share this here.
Happy to answer any questions people have about Flutter and our experience with it. I also plan to write a more detailed technical blog post in a couple of months.
Neat example, thanks! I hadn't known TS had that option. Array access was actually exactly the example that came to mind for me in a related discussion:
https://news.ycombinator.com/item?id=41076755
I wonder how widely used that option is. As I said in that other comment, it feels to me like the sort of thing that would produce errors all over the place, and would therefore be a real pain to migrate to. (It'd be just fine if the language semantics were that out-of-bounds array access throws, but that's not the semantics JS has.) I don't have a real empirical sense of that, though.
That is in fact one of the new features in this 9.0 release! There's a new less-dense default, and the previous dense layout remains an option (which for myself I immediately turned on).
We're also planning to give a wider array of options in an upcoming release, including independently setting the line-height and the font size. It's a lot of work to get even two options to both have a reasonable layout throughout the UI, though (among other things, it involved changing a lot of hardcoded values in px to be in relative units), which is why only the two-way switch made it into this week's release.
Flow is not sound. They have the ambition of trying to be sound (which I appreciate), but they've never accomplished it.
I went looking for where on their website they claim to be sound. There's definitely some misleading wording here:
https://flow.org/en/docs/lang/types-and-expressions/#toc-sou...
but if you read the whole section, it ends up also acknowledging that it's not entirely sound.
Definitely to get the most ergonomic programming experience, while also having a sound type system, you'd need to change some of the semantics of the language.
A prime example is that if you index into an array of type `T[]`, JS semantics mean the value you get back could be undefined as well as a `T`. So to describe existing JS semantics in a sound type system, the type would have to be `T | undefined`, which would be a big pain. Alternatively you could make the type `T` and have that be sound, but only if you make the runtime semantics be that an out-of-bounds access throws instead of returning undefined.
That's true but misleading: if "any" and "unknown" were the only types, then "any" would be indistinguishable from "unknown" and you'd really have just the one type. Which makes the type system sound because it doesn't say anything.
If your type system has at least two types that aren't the same as each other, then adding "any" makes it unsound right there. The essence of "any" is that it lets you take a value of one type and pretend it's of any other type. Which is to say that "any" is basically the purified form of unsoundness.
Yeah, Flow had the ambition to be sound but has never accomplished it.
If you read the Flow codebase and its Git history, you can see that it's not for lack of trying, either — every couple of years there's an ambitious new engineer with a new plan for how to make it happen. But it's a real tough migration problem — it only works if they can provide a credible, appealing migration path to the other engineers across Facebook/Meta's giant JS codebase. Starting from a language like JS with all the dynamic tricks people use there, that's a tough job.
(And naturally it'd be even harder if they were trying to get any wider community to migrate, outside their own employer.)
There's nothing arcane or particularly theoretical about soundness. It means that if you have an expression of some type, and at runtime the expression evaluates to a value, the value will always be of that type.
For example if you have a Java expression of type MyClass, and it gets evaluated, then it must either throw (so that it doesn't produce any value) or produce a value of type MyClass: either an instance of MyClass, or of one of its subclasses, or null. It will never produce an instance of some other class, or an int, or anything else that isn't a valid value for the type MyClass.
In addition to helping human readers reason about the code, a sound type system is a big deal for a compiler: it makes it possible to compile the code AOT to fast native code, without inserting a bunch of runtime checks and dynamic dispatching to handle the fact that inevitably some of the types (but you don't know which) are wrong.
The compiler implications are what motivated the Dart language's developers to migrate from an unsound to a sound type system a few years ago:
https://dart.dev/language/type-system#the-benefits-of-soundn...
so that they could compile Flutter apps AOT. This didn't require anyone to make their code resemble what you'd do in a theorem prover — it just means that, for example, all casts are checked, so that they throw if the value doesn't turn out to have the type the cast wants to return.
TypeScript is unsound because when you have an expression with a type, that tells you nothing at all for sure about what the value of the expression can be — it might be of that type, or it might be anything else. It's still valuable because you can maintain a codebase where the types are mostly accurate, and that's enough to help a lot in reading and maintaining the code.
When a language's type system is sound, that means that if you have an expression with type "string", then when you run the program the expression's value will only ever be a string and never some other sort of value.
Or stated more abstractly: if an expression has type T, and at runtime the expression evaluates to a value v, then v has type T.
The language can still have runtime errors, like if you try to access an array out of bounds. The key is that such operations have to give an error — like by throwing, so that the expression doesn't evaluate to any value at all — rather than returning a value that doesn't fit the type.
Both TypeScript and Flow are unsound, because an expression with type "string" can always turn out to evaluate to null or a number or an object or anything else. Flow had the ambition to be sound, which is honorable but they never accomplished it. TypeScript announced up front that they didn't care about soundness:
https://www.typescriptlang.org/docs/handbook/type-compatibil...
Soundness is valuable because it makes it possible to look at the types and reason about the program using them. An unsound type-checker like TypeScript or Flow can still be very useful to human readers if most of the types in a codebase are accurate, but you always have to keep that asterisk in the back of your head.
One very concrete consequence of soundness that it makes it possible to compile the code to fast native code. That's what motivated Dart a few years ago to migrate from an unsound type system to a sound one:
https://dart.dev/language/type-system
so that it could AOT-compile Flutter apps for speed.
One of the things that's made us at Zulip very happy with Flutter is that it's an honest-to-goodness functioning open-source project. The issue tracker is out in the open, as are the PRs and code reviews. You can (and we did) show up with bug reports and someone whose job is to develop Flutter will read it and make a cogent reply. And getting a change merged in Flutter is however easy or hard it is to do the change well, but no harder — the codebase is high-quality, and the maintainers are responsive. It's at quite a high percentile among all the open-source projects I've interacted with.
I don't know much about Kotlin Multiplatform or Compose Multiplatform. But from what I've seen, the Android group in general doesn't live up to that high standard as an open-source project. You can report bugs and might get a reply, but can't really participate in the discussion; the public bug often gets linked to a bunch of internal bugs you can't see, and the code reviews for any fix happen in private.
(I lead the Zulip mobile team, so the decision to use Flutter was primarily mine.)
I can definitely believe that that was an objection customers had, though. I just suspect that what it really meant was another way of expressing that there wasn't PMF - if the PMF were there, they'd have willingly paid, just like they do now for Notion or Slab or what have you.
And yeah, there was definitely some energy going into trying to tie the products together more (putting Paper docs in your Dropbox folders) - and when that finally shipped it sadly made the Paper experience worse, not better.