In a recent post, @evancz made a useful distinction between a native module which "binds" to basic platform facilities, on the one hand, and a native module which "wraps" larger Javascript libraries, on the other. Part of his point was that "binding" is, in a general sense, a positive thing for Elm, whereas "wrapping" has some dangers for the development of Elm (though it can be tempting).
I thought it might be useful to gather a little bit of data about existing native modules in light of that distinction -- both modules which are in the package repository, and modules which were submitted for native review but not whitelisted.
In doing this classification, I realized that there is actually a third category for native modules, which I would call "implementing". This covers cases where a genuinely Elm API is implemented in native code. Consider, for instance, the `Task` module in elm-lang/core. It doesn't really bind a specific platform facility as such. Instead, it implements a genuinely Elm API by using native code. You could say the same of `List`, `Dict`, `Array`, etc.
So, I thought it might be useful to get a sense of which modules fall into each of the three categories ("binding", "wrapping" and "implementing"), and count how many.
Here is what I have come up with. I've mostly categorized each package in terms of it's dominant characteristic, but in one case I've used two categories -- for elm-lang/core, there are clearly important elements which are "binding" and important elements which are "implementing".
Before I give the list, here are a few interesting cases:
johnpmayer/elm-linear-algebra
This was one package whose classification I thought was doubtful. On the one hand, it does make use of a Javascript library. However, it does so more in the spirit of porting than wrapping. In another sense, it is a kind of binding, since it does use some Javascript APIs not otherwise exposed in Elm. However, in the end, it seemed to me that it was more about "implementing" -- both the Javascript used and the bindings used were essentially subordinate to the authentic purpose of implementing an Elm API. But you could think that one should be classified differently.
Fresheyeball/elm-http-server
This module provides a binding to the Nodejs `http` object. You *could* conceivably think of this as wrapping. However, it seemed to me that this is actually more like binding -- you would not, for instance, really expect Elm code to re-implement an http server based on network primitives -- instead, you'd probably consider basic http serving part of the platform you're binding to. However, you certainly could think differently about that.
laszlopandy/elm-console
The elm-console package had previously used ports, but switched to using some native code in order to simplify the API for clients. This might be an interesting example of a case where ports were a feasible, but not optimal, implementation.
smiley325/elm-geocoding
This module is clearly a case of "wrapping", since it wraps the Google Maps API. However, it occurred to me that this (or other cases like it) might represent a case where wrapping is less bad than usual. That is, sometimes cloud-based APIs come with matching Javascript libraries. Ideally, it might be nice to port them to genuine Elm code, rather than wrapping them. However, it might not always be realistic.
So, that having been said, here are the lists.
In the package repository
coreytrampe/elm-vendor Binding
elm-community/elm-history Binding
elm-lang/core Binding, Implementing
evancz/elm-effects Binding
evancz/elm-http Binding
evancz/elm-markdown Wrapping
evancz/task-tutorial Binding
evancz/virtual-dom Wrapping
joefiorini/elm-mousetrap Wrapping
johnpmayer/elm-webgl Binding
johnpmayer/elm-linear-algebra Implementing
jwmerrill/elm-animation-frame Binding
laszlopandy/elm-console Implementing
maxsnew/lazy Implementing
mgold/elm-socketio Binding
michaelbjames/elm-benchmark Implementing
NoRedInk/elm-rails Implementing
seliopou/elm-d3 Wrapping
TheSeamau5/elm-history Binding
ThomasWeiser/elmfire Wrapping
So, considering things which made it into the package repository, my count would be as follows:
Binding 10
Wrapping 5
Implementing 6
Submitted for native review but not accepted
simonh1000/file-reader Binding
dasch/elm-page-visibility Binding
dimsmol/vdom-to-html Wrapping
unknownloner/elm-webgl Binding
JustusAdam/elm-path Binding
newlandsvalley/elm-webmidi Binding
Fresheyeball/elm-http-server Binding
Fresheyeball/elm-chartjs Wrapping
jessitron/elm-cookie Binding
suryagaddipati/elm-html Wrapping
shutej/rfc3339 Binding
imeckler/expression Wrapping
piotrcyr/elm-filereader Binding
GlenDC/elm-editor-keyboard Binding
GlenDC/elm-mouse-extra Binding
koyachi/elm-sha Wrapping
TheSeamau5/elm-time-extra Binding
suzuki-shin/elm-chartjs Wrapping
zimbatm/elm-gamepad Binding
uehaj/IntRange Implementing
brandonrayhaun/FormatNumber Binding
smiley325/elm-geocoding Wrapping
rgrempel/elm-web-api Binding
rgrempel/elm-ticker Binding
rgrempel/elm-cookies Binding
notnew/access Implementing
bmatcuk/elm-webaudio Binding
imeckler/iterator Implementing
pchiusano/elm-execute Implementing
jonathanhefner/elm-range Implementing
TheSeamau5/elm-loop Implementing
avh4/jsdiff Wrapping
imeckler/stage Binding
philopon/console Binding
So, by my count that works out to:
Binding 20
Wrapping 8
Implementing 6
And, the total, if you consider both in and out of the package repository, would be:
Binding 30
Wrapping 13
Implementing 12
As far as drawing conclusions from this goes, I think it confirms that wrapping can be a temptation. However, I think it suggests that module authors were not drawn towards wrapping like a moth to the flame -- it was not the dominant purpose of native modules, either inside or outside the repository. Other than that, I think it's interesting data to keep in mind when thinking about the reasons why people write native modules -- there are a variety of insights you might take from it.