aboutsummaryrefslogtreecommitdiffstats
path: root/posts/elm-everything-broken.md
blob: a9978d1c743e42dd4060892c38a1d1b6eda6ac67 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
Title: Everything Is Broken (Installing Elm Edition)
Author: bnewbold
Date: 2016-05-06

*This post is a narrative rant (in the same vein of Dan Luu's ["Everything is
Broken"][danluu] post) about my problems one afternoon getting a Fancy New
Programming Language to work on my laptop. [Skip down](#nowwhat) for my
thoughts on solutions.*

[danluu]: https://danluu.com/everything-is-broken/

<br />

<div class="sidebar">
<a href="https://www.youtube.com/watch?v=oYk8CKH7OhE">
<img src="/static/fig/elm-evan-talk.jpg" alt="photo of E.C. giving an elm talk" width="240px" />
</a>
</div>

A few folks here at the [Recurse Center][rc] had nice things to say about the
[Elm programming language][rc], and after watching Evan Czaplicki's ["Let's Be
Mainstream" talk][0] I got pretty excited also. "Let's try Elm!" and the
adventure begins.

[rc]: https://recurse.com/
[elm]: https://elm-lang.org
[0]: https://www.youtube.com/watch?v=oYk8CKH7OhE

First I go in the front door with the [install page][install], which redirects
me to a page which instructs me to use npm.

[install]: http://elm-lang.org/install

Hrm, I've had painful experiences with npm in the past, and anyways, isn't
the Elm compiler implemented in Haskell? Why are they using a package
management tool for Javascript libraries for installation? No thanks, I'll
[build from source][build], following these directions from the `README.md`:

[build]: https://github.com/elm-lang/elm-platform

    # If you are on LINUX, you need to install a dependency of elm-repl.
    # Uncomment the following line and run it.
    # sudo apt-get install libtinfo-dev

    # if you are on windows, or some other place without curl, just download this file manually
    curl https://raw.githubusercontent.com/elm-lang/elm-platform/master/installers/BuildFromSource.hs > BuildFromSource.hs

    runhaskell BuildFromSource.hs 0.16

<!-- If you don't know how to clone a repo, are you really going to know how to
get `cabal` set up, open a terminal and paste a command, etc? -->

Weird that these build-from-source directions involve curl-to-file magics. Why
not just clone the repo and use a Makefile, which is roughly the same number of
commands to enter? I clone repo and run the build script from there instead.
I'm on Debian stable ("jessie"), so installing GHC (the most popular way to use
Haskell) is easy. I seem to have a recent-ish version of cabal:

<pre class="terminal">
bnewbold@eschaton$ cabal --version
cabal-install version 1.20.0.3
using version 1.20.0.2 of the Cabal library 
</pre>

... so I think i'm ready to go ahead run the `BuildFromSource.hs` script:

<pre class="terminal">
bnewbold@eschaton$ runhaskell ~/src/elm-platform/installers/BuildFromSource.hs 0.16

GHCi runtime linker: fatal error: I found a duplicate definition for symbol
_hs_bytestring_long_long_uint_hex
whilst processing object file
/home/bnewbold/.cabal/lib/x86_64-linux-ghc-7.6.3/bytestring-0.10.6.0/HSbytestring-0.10.6.0.o
This could be caused by:
* Loading two different object files which export the same symbol
* Specifying the same object file twice on the GHCi command line
* An incorrect `package.conf' entry, causing some object to be
    loaded twice.
GHCi cannot safely continue in this situation.  Exiting now.  Sorry.
</pre>

Uh-oh! Not sure what that means. A search returns [a FAQ entry][faq-entry]
about "Duplicate Directions" which talks about linker flags, but *I* didn't
compile anything by hand so it doesn't seem like *I* have done anything wrong.
I'll try updating `cabal`'s package index:

[faq-entry]: https://wiki.haskell.org/GHC/FAQ#Duplicate_Definitions

<pre class="terminal">
bnewbold@eschaton$ cabal update
Downloading the latest package list from hackage.haskell.org
cabal: Failed to download
http://hackage.haskell.org/packages/archive/00-index.tar.gz : ErrorMisc "Error
HTTP code: 502"
</pre>

No go! At first I assumed I was doing something wrong, or maybe the WiFi was
flakey, but it turns out that the Hackage package repository [is
down](https://status.haskell.org/):

> hackage.haskell.org down
>
> Full Service Disruption
>
> [Investigating] The hackage server is in an out of memory condition. We're investigating.

<div class="sidebar">
As far as I know, the only Debian packages out of the huge stable archive which
don't receive security updates are web browsers, mediawiki, and all V8/node.js
packages and libraries.
</div>

Well, it wasn't even clear if updating my package index would fix the problem,
it was just a wild guess. I'll give up and try npm, which these days has been
[sort of packaged][deb-node] in Debian. I'll just uninstall npm when I'm done
installing Elm. I install with `apt` (which pulls in a huge list of `nodejs`
packages), but then can finally run:

[deb-node]: https://www.debian.org/releases/stable/amd64/release-notes/ch-information.en.html#libv8

<pre class="terminal">
bnewbold@eschaton$ npm install elm
|
> elm@0.16.0 install /home/bnewbold/bin/node_modules/elm
> node install.js

sh: 1: node: not found
npm WARN This failure might be due to the use of legacy binary "node"
npm WARN For further explanations, please read
/usr/share/doc/nodejs/README.Debian

npm ERR! elm@0.16.0 install: `node install.js`
npm ERR! Exit status 127
npm ERR! 
npm ERR! Failed at the elm@0.16.0 install script.
npm ERR! This is most likely a problem with the elm package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     node install.js
npm ERR! You can get their info via:
npm ERR!     npm owner ls elm
npm ERR! There is likely additional logging output above.

[...]
<!--
npm ERR! System Linux 3.16.0-4-amd64
npm ERR! command "/usr/bin/nodejs" "/usr/bin/npm" "install" "elm"
npm ERR! cwd /home/bnewbold/bin
npm ERR! node -v v0.10.29
npm ERR! npm -v 1.4.21
npm ERR! code ELIFECYCLE
npm ERR! 
npm ERR! Additional logging details can be found in:
npm ERR!     /home/bnewbold/bin/npm-debug.log
npm ERR! not ok code 0
-->
</pre>

<div class="sidebar">
Later I learned that I could have instead installed the nodejs-legacy
package, which just installs a symlink /usr/bin/node which points to
/usr/bin/nodejs.
</div>

Looks like Debian wants to refer to node.js as "`nodejs`" while npm/Elm expects
it to be "`node`". Ok, I create this shim named `node` in my `~/bin` directory
(which is on my `$PATH`), and make it executable:

<pre class="terminal">
#!/bin/sh
nodejs $*
</pre>

Now I can do things like:

<pre class="terminal">
bnewbold@eschaton$ node --version
v0.10.29
</pre>

Great! Now let's install Elm with npm!

<pre class="terminal">
bnewbold@eschaton$ npm install elm

> elm@0.16.0 install /home/bnewbold/bin/node_modules/elm
> node install.js

Downloading Elm Reactor assets from https://dl.bintray.com/elmlang/elm-platform/0.16.0/elm-reactor-assets.tar.gz
Error communicating with URL https://dl.bintray.com/elmlang/elm-platform/0.16.0/linux-x64.tar.gz Error: CERT_UNTRUSTED
npm WARN This failure might be due to the use of legacy binary "node"
npm WARN For further explanations, please read
/usr/share/doc/nodejs/README.Debian

npm ERR! elm@0.16.0 install: `node install.js`
npm ERR! Exit status 1
npm ERR! 
npm ERR! Failed at the elm@0.16.0 install script.
npm ERR! This is most likely a problem with the elm package,
npm ERR! not with npm itself.
npm ERR! Tell the author that this fails on your system:
npm ERR!     node install.js
npm ERR! You can get their info via:
npm ERR!     npm owner ls elm
npm ERR! There is likely additional logging output above.

npm ERR! System Linux 3.16.0-4-amd64
npm ERR! command "/usr/bin/nodejs" "/usr/bin/npm" "install" "elm"
npm ERR! cwd /home/bnewbold/bin
npm ERR! node -v v0.10.29
npm ERR! npm -v 1.4.21
npm ERR! code ELIFECYCLE
npm ERR! 
npm ERR! Additional logging details can be found in:
npm ERR!     /home/bnewbold/bin/npm-debug.log
npm ERR! not ok code 0
</pre>

Whoops. There is [a github issue][elm-issue] that has some tips about this: the
recommended solutions online are basically "upgrade node.js and npm" or
"disable SSL security". That doesn't sound great, but i'm feeling very
impatient at this point so I tried disabling SSL checks with `npm config set ca
null`, but even then I get the certificate error. Enough of that approach:

[elm-issue]: https://github.com/elm-lang/elm-platform/issues/100

<pre class="terminal">
sudo apt remove npm nodejs
rm ~/bin/node
</pre>

Poking around a bit more, I find the promisingly-titled "`fubar.sh`" script in
the `elm-platform` repo cloned earlier, which seems to nuke the local user's
Haskell (`cabal`) stuff (but doesn't muck with system-wide package, eg `apt`
managed GHC libraries).  After running `fubar.sh` the `BuildFromSource.hs`
script seems to start working (success!), but Hackage is still down (now, an
hour after I started this process, approaching 5 hours total downtime), so I
can't install dependencies.

I search for a hackage mirror and find that [FPComplete runs
one](https://www.fpcomplete.com/blog/2015/03/hackage-mirror).  In addition to
their configuration instructions I had to set `remote-repo-cache` in my
`~/.cabal/config`, like so:

    remote-repo: hackage.fpcomplete.com:http://hackage.fpcomplete.com/
    remote-repo-cache: /home/bnewbold/.cabal/packages-fpcomplete

After that I `cabal update` and run the `BuildFromSource.hs` script again. It
fails to build the dependency `websockets-snap`, but I try installing that
regularly (`cabal install websockets-snap`) and that works, and then all the
dependencies with BuildFromSource.hs build. Yay!

I still get an error with compiling Elm itself though:

    src/Elm/Package.hs:60:25:
        Not in scope: `<$>'
        Perhaps you meant `</>' (imported from System.FilePath)

It [sounds like](https://github.com/elm-lang/elm-platform/issues/30) I am using
an out of date version of GHC (the Haskell compiler): I have 7.6.3 (the version
that comes with Debian), and Elm wants 7.10. Fair enough: unlike a package
manager, I can see how new features in the compiler would be helpful, and when
I go back and check this was outlined in the README. Fortunately there is a new
version of GHC in the `jessie-backports` repo, so:

<pre class="terminal">
sudo apt install -t jessie-backports ghc
</pre>
   
And now:

<pre class="terminal">
bnewbold@eschaton$ cabal --version
cabal-install version 1.22.6.0
using version 1.22.5.0 of the Cabal library 
bnewbold@eschaton$ ghc --version
The Glorious Glasgow Haskell Compilation System, version 7.10.3
</pre>

After that, I'm miraculously able to compile. Horray! I add
`~/bin/Elm-Platform/0.16/.cabal-sandbox/bin` to my `$PATH` and I can run:

<pre class="terminal">
bnewbold@eschaton$ elm-repl 
---- elm repl 0.16.0 -----------------------------------------------------------
:help for help, :exit to exit, more at <https://github.com/elm-lang/elm-repl>
--------------------------------------------------------------------------------
> 
</pre>

Success!

To be clear, once I got this far I had few or no problems with Elm. The
language seems reasonably clean and well documented for it's maturity, and it
was only this install procress that felt horribly broken.

Throughout this debugging process I had problems with both my external [Lenovo
Thinkpad USB keyboard][keyboard] glitching (workaround: unplug and re-plug) and
with WiFi at the Recurse Center disconnecting (workaround: reset wifi card).
Others here using GNU/Linux have the same wifi problems, but we don't have a
fix yet... something to do with wireless regulatory zones.

It's not all pain though; installing the `elm.vim` syntax highlighting plugin
was very easy, just a single `git clone` into `~/.vim/bundle/`.

[keyboard]: https://www.amazon.com/Lenovo-ThinkPad-Compact-Keyboard-TrackPoint/dp/B00F3U4TQS

<a name="nowwhat" />

So Now What?
----------------

There was a great long-form blog post by Sam Boyer a few months ago ("[So You
Want To Write a Package Manager][5]") which describes the jumble of *system*,
*language*, and *project* dependency manager problems I experienced above. Sam
basically concludes that Rust's project/language dependency manager (Cargo)
both encapsulates best practices for dependable and reproducible builds, while
still allowing rapid evolution of a package "ecosystem". Cargo was designed by
[Yehuda Katz and Carl Lerche][6], and along with the
[crates.io](https://crates.io) archive it does sound very nice. My personal
feeling are usually that system-wide package managers (like Debian's `apt`) are
underappreciated by many young-but-not-bleeding-edge projects, but acknowledge
that there probably is also a need for higher tempo cross-platform project
dependency mangement for non-library projects (eg, desktop applications and web
apps).

Ironically (given the difficulty I had installing it), the Elm language's
package manager has a great `diff` tool for checking that any changes in the
API conform to the [documented][8] [semantic versioning][semver] conventions.
For [example](https://gist.github.com/badboy/a302dd0c9020e5759240):

    $ elm-package diff evancz/elm-html 3.0.0 4.0.2
    Comparing evancz/elm-html 3.0.0 to 4.0.2...
    This is a MAJOR change.
    
    ------ Changes to module Html.Attributes - MAJOR ------
    
        Removed:
            boolProperty : String -> Bool -> Attribute
            stringProperty : String -> String -> Attribute
    
    
    ------ Changes to module Html.Events - MINOR ------
    
        Added:
            type alias Options =
                { stopPropagation : Bool, preventDefault : Bool }
            defaultOptions : Html.Events.Options
            onWithOptions : String -> Html.Events.Options -> Json.Decode.Decoder a -> (a -> Signal.Message) -> Html.Attribute

This API change information is then used to *[programatically enforce][7]* the
semantic versioning rules for submissions to the Elm language library archive
and prevent a whole class of simple but annoying breakages due to unexpected
API changes. It can't detect *every* breaking change (eg, those which are
internal), but it can detect enough to be worth the effort.

Another option for detecting fixing breakage is integration testing at the
package ecosystem level. Debian's [reproducible builds][repro] effort has built
out one such system, and continues to catch thousands of "failed to build from
source" (FTBFS) bugs along the way. Dan Luu's ["Everything is Broken"][danluu]
blog post (which to some degree inspired this one) mostly focused on the lack
of quality tests for many contemporary software projects, and proposed the use
of smart fuzzing and heuristically-generated tests to work around the huge
technical debt this represents. As far as I can tell there are hardly ever
automated integration tests for entire package archives; blame for dependency
problems is usually attributed to a bug in one package (instead of being seen
as a systemic failure), and such problems are seen as an unavoidable cost of
rapid and distributed development.

I think we can do better. For example, some contemporary languages test
compiler changes for regressions against a broad snapshot of public code
written in that language; I know Go and Rust specifically do this to identify
problems before compiler releases, and the Python community occasionally runs
tests against the PyPi archive when considering syntax changes or feature
deprecations. And Debian's unstable and testing archives enforce a waiting
period so that human testers can turn up conflicts before packages can be moved
into stable or backports. I think we can and should automate these processes as
much as possible, and give direct feedback to library developers and package
maintainers when they push updates. Commercial Continuous Integration services
like Travis CI and Circle CI should offer more common target platforms (eg, ARM
architecture, more versions of distributions), and if they don't we should
build and host our own testing infrastructure. CI builds scripts should closely
match the official installation instructions for a given platform, so we catch
problems with those instructions quickly.

We have more and more developers in the world wasting more and more days
wrangling with dependency hell, but despite that I think we're closer than ever
to taming the beast.

[5]: https://medium.com/@sdboyer/so-you-want-to-write-a-package-manager-4ae9c17d9527
[6]: https://mail.mozilla.org/pipermail/rust-dev/2014-March/009090.html
[7]: https://github.com/elm-lang/elm-package/blob/master/README.md#version-rules
[8]: https://github.com/elm-lang/elm-package/blob/master/README.md#publishing-updates
[semver]: http://semver.org/
[appcheck]: http://www.linuxfoundation.org/collaborate/workgroups/lsb/linux-application-checker-getting-started
[repro]: https://tests.reproducible-builds.org/debian/reproducible.html

<hr />

#### Follow up June 18th, 2016

A few days after writing this post, a new version of Elm (0.17) was 
[released](http://elm-lang.org/blog/farewell-to-frp). The new version made some
big changes, like abandoning the [Functional Reactive Programming][frp]
paradigm. I found that many of the new tutorials weren't working with my 0.16
install, so I tried to upgrade.  After an hour or two of following the
directions above, I gave up and installed npm on a temporary Debian unstable
("sid") virtual machine. Once I added a `node` symlink to `nodejs`, I was able
to install `elm` with npm and develop remotely using `screen` and
`elm-react`. Frustrating!

[frp]: https://en.wikipedia.org/wiki/Functional_reactive_programming

<br />
<center style="font-size: smaller;">
<img src="/static/fig/status_haskell_broken.png" width="600px" />
<br />
Also, status.haskell.org seems to be broken again today, though
the hackage website works.
</center>

On the plus side, my USB keyboard problem seems to have been due to the cheap
random USB cable I was using. Since replacing it I haven't had the bad
flakey-ness, though now after my laptop awakes from sleep the pointer and
keyboard work but the middle mouse button ("paste" on UNIX) does not.
Workaround: plug and unplug the whole keyboard.