[001.3] Elm Packages


An overview of the Elm package tool, ecosystem, and some related sites.

Subscribe now

Elm's Packages Site and infrastructure is some of the best around. You can look there to find packages that do all manner of fun things, and it will tell you if you're browsing an outdated version of the documentation.

Details

Enforced Semantic Versioning

One of my favorite features is the fact that semantic versioning is enforced in the package management tool itself. This is made possible, once again, by the fantastic type system.

If you introduce a change to any public function that already existed, which affects the type signature, you must bump the MAJOR version.

An Example

Imagine you release version 1.0.0 of your package and expose a foo function that takes a String parameter and produces a Bool. After a while, you decide that it would really work best if it took two string parameters and produced a Bool.

You make the change in your code. At this point, anyone using your package that upgrades without changing their code would be left in a position where their code no longer works correctly.

In a lesser package manager, this is just Life As Usual, and people get on with it. Elm, however, is not composed of barbarians. Consequently, you cannot even publish this modified package until you bump the MAJOR version, so now you would be releasing version 2.0.0. Typically, I wouldn't have this new version come in by default, as elm package install would specify the version to a single major version.

Consequently, I must manually upgrade to the next major version, and at that point I will of course be left with code that doesn't compile due to this change. However, I know to expect something since I saw that your package was upgraded to a new major version, which implies breaking changes.

Additional Awesomeness

Also, because of all of the rich type information and documentation in each package, it's possible to see detailed information on what changed between 2 specified versions of a package:

elm package diff elm-lang/core 4.0.0 5.0.0

This is particularly interesting, as this is the version bump from 0.17 to 0.18 for the core package. I've included this command's full output at the end of this document so you can easily see what that looks like if you don't feel like running it yourself.

Elmalytics

Elmalytics is a website for viewing the Elm package and project ecosystem at a high level, seeing nice things like a graph of the number of Elm repos created on GitHub over time, or the most-starred Elm projects on GitHub.

There's also a great tool (linked as Fancy Search in the main package site's sidebar) for searching all Elm packages for functions by name or approximate type signature. Think of like Hoogle, if you're familiar with the Haskell world.

Summary

This was just a brief discussion of the package ecosystem. There's a lot more interesting details around it, but I figured it wasn't worth digging in too deeply before we start building our first HTML app - we'll do that in the next episode. See you soon!

Output for diff of elm-lang/core 4.0.0 -> 5.0.0

Comparing elm-lang/core 4.0.0 to 5.0.0...
This is a MAJOR change.

------ Added modules - MINOR ------

    Tuple


------ Changes to module Basics - MAJOR ------

    Added:
        never : Basics.Never -> a

    Removed:
        fst : (a, b) -> a
        snd : (a, b) -> b


------ Changes to module Bitwise - MAJOR ------

    Added:
        shiftLeftBy : Int -> Int -> Int
        shiftRightBy : Int -> Int -> Int
        shiftRightZfBy : Int -> Int -> Int

    Removed:
        shiftLeft : Int -> Int -> Int
        shiftRight : Int -> Int -> Int
        shiftRightLogical : Int -> Int -> Int


------ Changes to module Json.Decode - MAJOR ------

    Added:
        field : String -> Json.Decode.Decoder a -> Json.Decode.Decoder a
        index : Int -> Json.Decode.Decoder a -> Json.Decode.Decoder a
        lazy : (_Tuple0 -> Json.Decode.Decoder a) -> Json.Decode.Decoder a
        map2 : (a -> b -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder value
        map3 : (a -> b -> c -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder value
        map4 : (a -> b -> c -> d -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder value
        map5 : (a -> b -> c -> d -> e -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder value
        map6 : (a -> b -> c -> d -> e -> f -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder value
        map7 : (a -> b -> c -> d -> e -> f -> g -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder g -> Json.Decode.Decoder value
        map8 : (a -> b -> c -> d -> e -> f -> g -> h -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder g -> Json.Decode.Decoder h -> Json.Decode.Decoder value
        nullable : Json.Decode.Decoder a -> Json.Decode.Decoder (Maybe.Maybe a)

    Removed:
        := : String -> Json.Decode.Decoder a -> Json.Decode.Decoder a
        customDecoder : Json.Decode.Decoder a -> (a -> Result.Result String b) -> Json.Decode.Decoder b
        object1 : (a -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder value
        object2 : (a -> b -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder value
        object3 : (a -> b -> c -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder value
        object4 : (a -> b -> c -> d -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder value
        object5 : (a -> b -> c -> d -> e -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder value
        object6 : (a -> b -> c -> d -> e -> f -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder value
        object7 : (a -> b -> c -> d -> e -> f -> g -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder g -> Json.Decode.Decoder value
        object8 : (a -> b -> c -> d -> e -> f -> g -> h -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder g -> Json.Decode.Decoder h -> Json.Decode.Decoder value
        tuple1 : (a -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder value
        tuple2 : (a -> b -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder value
        tuple3 : (a -> b -> c -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder value
        tuple4 : (a -> b -> c -> d -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder value
        tuple5 : (a -> b -> c -> d -> e -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder value
        tuple6 : (a -> b -> c -> d -> e -> f -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder value
        tuple7 : (a -> b -> c -> d -> e -> f -> g -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder g -> Json.Decode.Decoder value
        tuple8 : (a -> b -> c -> d -> e -> f -> g -> h -> value) -> Json.Decode.Decoder a -> Json.Decode.Decoder b -> Json.Decode.Decoder c -> Json.Decode.Decoder d -> Json.Decode.Decoder e -> Json.Decode.Decoder f -> Json.Decode.Decoder g -> Json.Decode.Decoder h -> Json.Decode.Decoder value

    Changed:
      - andThen : Json.Decode.Decoder a -> (a -> Json.Decode.Decoder b) -> Json.Decode.Decoder b
      + andThen : (a -> Json.Decode.Decoder b) -> Json.Decode.Decoder a -> Json.Decode.Decoder b



------ Changes to module List - MINOR ------

    Added:
        range : Int -> Int -> List Int


------ Changes to module Maybe - MAJOR ------

    Removed:
        oneOf : List (Maybe.Maybe a) -> Maybe.Maybe a

    Changed:
      - andThen : Maybe.Maybe a -> (a -> Maybe.Maybe b) -> Maybe.Maybe b
      + andThen : (a -> Maybe.Maybe b) -> Maybe.Maybe a -> Maybe.Maybe b



------ Changes to module Platform - MINOR ------

    Added:
        program : { init : (model, Platform.Cmd.Cmd msg),
                    update : msg -> model -> (model, Platform.Cmd.Cmd msg),
                    subscriptions : model -> Platform.Sub.Sub msg
                  } -> Platform.Program Basics.Never model msg
        programWithFlags : { init : flags -> (model, Platform.Cmd.Cmd msg),
                             update : msg -> model -> (model, Platform.Cmd.Cmd msg),
                             subscriptions : model -> Platform.Sub.Sub msg
                           } -> Platform.Program flags model msg


------ Changes to module Random - MAJOR ------

    Changed:
      - andThen : Random.Generator a -> (a -> Random.Generator b) -> Random.Generator b
      + andThen : (a -> Random.Generator b) -> Random.Generator a -> Random.Generator b



------ Changes to module Result - MAJOR ------

    Added:
        mapError : (x -> y) -> Result.Result x a -> Result.Result y a

    Removed:
        formatError : (error -> error') -> Result.Result error a -> Result.Result error' a

    Changed:
      - andThen : Result.Result x a -> (a -> Result.Result x b) -> Result.Result x b
      + andThen : (a -> Result.Result x b) -> Result.Result x a -> Result.Result x b



------ Changes to module Task - MAJOR ------

    Added:
        attempt : (Result.Result x a -> msg) -> Task.Task x a -> Platform.Cmd.Cmd msg

    Removed:
        andMap : Task.Task x (a -> b) -> Task.Task x a -> Task.Task x b
        fromMaybe : x -> Maybe.Maybe a -> Task.Task x a
        fromResult : Result.Result x a -> Task.Task x a
        toMaybe : Task.Task x a -> Task.Task never (Maybe.Maybe a)
        toResult : Task.Task x a -> Task.Task never (Result.Result x a)

    Changed:
      - andThen : Task.Task x a -> (a -> Task.Task x b) -> Task.Task x b
      + andThen : (a -> Task.Task x b) -> Task.Task x a -> Task.Task x b

      - onError : Task.Task x a -> (x -> Task.Task y a) -> Task.Task y a
      + onError : (x -> Task.Task y a) -> Task.Task x a -> Task.Task y a

      - perform : (x -> msg) -> (a -> msg) -> Task.Task x a -> Platform.Cmd.Cmd msg
      + perform : (a -> msg) -> Task.Task Basics.Never a -> Platform.Cmd.Cmd msg

Published on 05.08.2016