journal  photos  code  knowledge bnewbold.net

Everything Is Broken (Installing Elm Edition)

 
Fri 06 May 2016
bnewbold

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


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

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

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, following these directions from the README.md:

# 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

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:

bnewbold@eschaton$ cabal --version
cabal-install version 1.20.0.3
using version 1.20.0.2 of the Cabal library 

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

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.

Uh-oh! Not sure what that means. A search returns a 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:

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"

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:

hackage.haskell.org down

Full Service Disruption

[Investigating] The hackage server is in an out of memory condition. We're investigating.

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 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:

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.

[...]

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:

#!/bin/sh
nodejs $*

Now I can do things like:

bnewbold@eschaton$ node --version
v0.10.29

Great! Now let's install Elm with npm!

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

Whoops. There is a github 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:

sudo apt remove npm nodejs
rm ~/bin/node

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. 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 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:

sudo apt install -t jessie-backports ghc

And now:

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

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:

bnewbold@eschaton$ elm-repl 
---- elm repl 0.16.0 -----------------------------------------------------------
:help for help, :exit to exit, more at 
--------------------------------------------------------------------------------
> 

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 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/.

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") 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, and along with the 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 semantic versioning conventions. For example:

$ 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 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 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" 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.


Follow up June 18th, 2016

A few days after writing this post, a new version of Elm (0.17) was released. The new version made some big changes, like abandoning the Functional Reactive Programming 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!



Also, status.haskell.org seems to be broken again today, though the hackage website works.

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.