Remembering the native review process

I read Evan’s recollections of the history of native code with great interest, having experienced and thought about that history in some detail. There are two points on which my recollections differ from his, and I thought it might be useful to share some research which I have previously done with the respect to the native review process.

Evan recalls that the native review process was faced with a lot of “bindings to whatever.js”. This is not consistent with my recollection. In fact, in the context of a previous discussion, I did some research on this question. You can follow the link to the full post if you like, but I’ll summarize some key results here:

Looking at the packages considered for native review, one could discern three distinct purposes for native code:

  • Bindings to basic platform facilities (the Web API).
  • Wrappers around “whatever.js”
  • Implementing genuine Elm data structures that cannot be implemented in pure Elm (consider Task as a quick, familiar example).

Of the packages rejected in the native review process, my count was as follows:

  • Binding to basic platform facilities: 20
  • Wrapping “whatever.js”: 8
  • Implementing genuinely Elm data structures: 6

This data suggests that the native review process was not, in fact, flooded with “bindings to whatever.js”. Wrapping Javascript libraries was not the dominant purpose of the packages submitted to that process.

It is also interesting to review the purpose of the native code in packages that were available in the package repository. My count (at that time) was as follows:

  • Binding to basic platform facilities: 10
  • Wrapping “whatever.js”: 5
  • Implementing genuinely Elm data structures: 6

You will notice that the profile of the purposes of the accepted packages is roughly similar to the profile of the purposes of the rejected packages. This suggests that the failed attempts to contribute native code to the package repository were not motivated by factors fundamentally different from the successful attempts.

Why, then, did the native review process fail, if it was not due to “getting a lot of bindings to whatever.js”?

My recollection is that there were several people on the native review committee who eagerly put in the work necessary to achieve the purely technical goals of the review process – that is, ensuring that the quality of the code was sound in every way.

The difficulty was that the final decision as to whether a package would be accepted depended on additional considerations which I would characterize as “strategic” in nature. By “strategic” I mean considerations such as:

  • should this Web API be exposed at all?
  • is this a priority now, or should it wait for later?
  • might we want to expose this Web API in a different way in the future?
  • what effect would availability of this package have on the development of the Elm community generally?

Now, you might be thinking that these are good questions to ask – and, to a point, you’d be right about that. However, it was frustrating, both for members of the review committee and for package authors. The key frustration, I believe, is that it was genuinely difficult to actually discuss those questions. They are subtle questions which depend on a vision for the future of Elm, its framework, and its community that is difficult (and time-consuming) to fully articulate. Furthermore, they aren’t amenable to ordinary technical experimentation – you can’t, for instance, try one future of Elm in which certain capabilities are exposed now and another in which they wait for later and see which turns out better.

So, the final decision ended up turning on considerations which, for committee members and package authors, inevitably felt arbitrary. I don’t mean to say that the considerations were actually arbitrary – in fact, they were almost the complete opposite of arbitrary, since they were based on an extremely deep analysis. However, they weren’t really matters amenable to discussion, and for that reason they felt arbitrary.

Now, most programming languages solve this problem by not thinking quite so hard about the ideal shape for the development of their frameworks and their communities. Elm has achieved some unique results by doing things a little differently. This does lead to some frustrations, and there are surely some micro-improvements possible. However, it’s not easy to know which of Elm’s peculiarities are essential to its success, and these are matters which are very difficult to discuss in a way which proves helpful. (I may well have failed in this attempt).

7 Likes

I’m not sure there’s actually disagreement here! Evan’s post said:

I was on the review committee, and this is how I remember it too.

According to the numbers you looked up (really interesting to see, by the way - thanks for taking the time!) it looks like proposals would have more than doubled the number of “whatever.js” bindings in the ecosystem. That’s a lot if you ask me!

Instead of doubling, this number has decreased over time (since upon reflection, it was a mistake to accept so many in the first place - including NoRedInk/elm-rails, which I say even though I’ve worked on and used that one) and in 0.19 I expect that number to be either zero or one (for the wrapped markdown parser - I’m not sure if any of the pure Elm Markdown parsers are complete yet). I definitely consider that an improvement over the status quo!

1 Like

This is actually one of the few cases where I can say definitively that Elm’s policy on prohibiting Native code in elm-package has caused success. I say this because it’s directly responsible for what has become Elm’s biggest selling point to the front-end Web developers I’ve been talking to over the past several years: the ecosystem.

Elm’s Biggest Selling Point

I found this out accidentally, because of TypeScript. People used to tell me Elm sounded appealing because of things like catching bugs at build time, but now anyone who wants a type-checker can use TypeScript without having to learn a whole new language. And yet, those who pick up Elm still overwhelmingly prefer it TypeScript. Why? Is Elm’s type-checking just that much better?

Asking people this question led me to better understand the role elm-package has played in Elm’s success. It turns out a large proportion of JS devs today are most attracted to how Elm’s ecosystem offers a cohesive story for UI development on the Web. As one meetup attendee told me, “We didn’t switch to Elm because we were unhappy with React, we switched because we were unhappy with npm.” Another told me Elm’s ecosystem characteristics (small but high-quality, “there’s one way to do it”) should be at the top of the elm-lang.org website.

The permissiveness of the npm ecosystem means there are many ways to do any given thing, and many feel the decision-making burden this creates for users of the ecosystem is never repaid in the quality delta between the competing approaches. Because elm-package does not permit importing familiar JS libraries from npm and wrapping them, this problem has not migrated from npm to elm-package.

Why do I say that Elm’s policy caused this though? Couldn’t an alternate explanation be that Elm’s ecosystem is too young to possibly have this problem?

Empirical Results of the Alternative Policy

We actually have experimental data on this! PureScript is another purely functional ML family language that compiles to JavaScript. Coincidentally, its package ecosystem is almost exactly the same age as Elm’s. One key difference is that PureScript permits publishing Native-style JavaScript code.

What does PureScript’s package ecosystem look like after the same 4 years have passed?

  1. There are several competing ways to do rendering and state management, just like in npm. Here’s a PureScript user saying “I have a hard time to choose between them,” with 17 others +1ing the issue. We don’t see similar issues in Elm (despite the ecosystems being the same age) because elm-package prohibits publishing the JS code required to create these competing alternatives.
  2. There are multiple competing implementations of bedrock types like Promise, Observable, and even Signal. That Signal library’s README begins “This library is a competing Signaling implementation” - and it competes with a different PureScript implementation of Elm’s original Signal type from before Elm 0.17. Elm intentionally took Signals out of its ecosystem; PureScript cannot.
  3. Many JavaScript libraries have been imported and wrapped wholesale, for example purescript-jquery, purescript-d3 and even purescript-redux-saga. The list goes on and on.

I think the data is very clear in this case. If elm-package allowed publishing arbitrary JS bindings, Elm’s ecosystem would look like PureScript’s: many wrapped JS libraries, many library decisions to make as a prerequisite for doing basic things like rendering and asynchronous effects, and so on. Making the case that this would somehow not have happened to Elm is, I think, extraordinarily difficult.

I’d say “no publishing arbitrary JS in elm-package“ is a design decision which history has proven about as unequivocally correct for Elm as type inference and avoiding runtime exceptions. It is directly responsible for what, to many JS programmers familiar with the alternative path, has become Elm’s most appealing characteristic.

16 Likes

Well, I am disagreeing with something …

I should say that your phrase “cohesive story for UI development on the Web” is wonderful – it expresses much more pithily than I did the unique degree to which Elm thinks strategically about the evolution of the language, framework and community together. It is helpful, I think, for anyone developing for Elm to understand this. It is very deeply baked in to how Elm does things. And, it has been key to the distinctive kind of success which Elm has achieved. (Though, to be fair, the approaches adopted by other languages have their own advantages, and there isn’t only one kind of success).

So, what am I disagreeing with? Basically, my recollection is that this focus on a “cohesive story” was used not just to reject “bindings to whatever.js”, but also to reject genuine, and technically sound, efforts to expose parts of the Web API, or to create genuinely Elm data structures that require bits of native implementation. Thus, I don’t accept the argument that the native review process failed because the potential contributions made too much use of “bindings to whatever.js”. To be clear, I am not mourning the loss of “bindings to whatever.js”. What I disagree with is blaming “bindings to whatever.js” for the failure of the native review process.

Was it possible to reject “bindings to whatever.js” and accept other contributions? Of course. Was it possible to make sure that those other contributions were technically sound? Yes, several committee members were doing that work, and expressed frustration when technically sound packages were not finally approved. Was it possible to have a discussion of the strategic reasons why a package might not fit Elm’s “cohesive story?” Well, perhaps not – my thesis is that the native review process failed because (a) Elm is conceived of in a way that makes that kind of discussion inevitable if native code is to be published; but (b) it was (and, no doubt, remains) too difficult and time-consuming to undertake.

I am conscious of the fact that this is a very small – perhaps even uninteresting – claim about a bit of history that will not long be remembered. Yet, I dislike how those potential contributions are being remembered. They were not, for the most part, foolish attempts to infect Elm with Javascript libraries. They were genuine attempts to contribute to Elm. The native review process failed, but it was not because of the nature of the potential contributions.

14 Likes

Thank you for that thoughtful recollection.

Briefly, I’d like to note the other side of the coin: being the maintainer of a native library. In my case that was elm-socketio and yes, it falls into the whatever.js category, except that it was approved for one version of Elk and then not approved for the next. It was frustrating to not be able to continue supporting my users, and to have to abandon the project, even though it made sense as to why.

More recently, I’ve been shepherding changed to core’s Random module so that elm-test doesn’t need my third-party library. That has had similar levels of uncertainty, delays, and worrying about code being examined under a microscope. (To Evan’s credit, everything is merged now.)

On the flip side, it’s incredibly freeing to know that I can publish anything written in pure Elm and know that it will work and be pleasant for anyone. No approvals, no “sorry you did all this work and can’t publish it”, and no “pretty please read the README and install this extra bit of JavaScript code somewhere in your project”. As a package author, I’m not mourning the native review process either.

8 Likes