Elm Compiler

repository·main·Indexed 27 days ago

https://github.com/elm/compiler

Documentation for the Elm functional programming language compiler, including installation and uninstallation guides for Linux, macOS, and Windows. Covers binary installation via GitHub Releases, npm global and local installation, and configuration of the elm.json file for both applications and packages.

Tokens
16.6K
Snippets
62
Records
115
Agent score
93%

What's inside elm-compiler

  1. Understand the Elm npm package structure

    main

    The Elm installer is distributed via npm using two types of packages:

    1. Main npm package: Named elm. This is the package end-users install.
    2. Binary npm packages: Scoped packages for specific platforms (e.g., @elm_binaries/darwin_arm64).

    The main package uses optionalDependencies to include binary packages. When a user installs elm, npm identifies the user's OS and CPU and installs only the matching binary package. If no match is found, the main package's install script provides an error message.

  2. Break module import cycles using unique identifiers

    main

    When two modules depend on each other (e.g., User needs Comment and Comment needs User), you cannot use recursive type alias definitions because Elm values are immutable. The most common and recommended solution is to use unique identifiers (like a String ID) instead of direct object references. This removes the cyclic dependency from the types and allows you to manage the relationship using a Dict.

    module Comment exposing (..)
    
    import Dict
    import User
    
    type alias Comment =
      { comment : String
      , author : User.Id
      }
    
    -- Use a Dict to manage the collection without cycles
    type alias AllComments = 
      Dict.Dict User.Id (List Comment)
  3. Distinguish between `type` and `type alias` for recursion

    main

    In Elm, type alias creates a shorthand for an existing type, while type creates a brand new, concrete type. Because type alias expands into its underlying structure, you cannot use a type alias to define a recursive structure directly (e.g., a record containing a list of itself), as the compiler would attempt to expand it infinitely.

    To implement recursive structures, you must use the type keyword to create a concrete type that breaks the expansion cycle.

  4. Use Debug.todo to handle incomplete pattern matching

    main

    When updating a large project with many functions affected by a type change, you can use Debug.todo to temporarily leave implementations incomplete. The Elm compiler recognizes Debug.todo within case expressions and will provide helpful runtime information if that branch is hit, including:

    1. The module name.
    2. The line numbers of the case expression.
    3. The specific value that triggered the TODO.

    This allows you to quickly satisfy the compiler while marking specific logic for later implementation.

    toName : User -> String
    toName user =
      case user of
        Regular name _ ->
          name
    
        Visitor _ ->
          Debug.todo "give the visitor name"
    
        Anonymous ->
          "anonymous"
  5. Replace Effects with Cmd

    main

    The evancz/elm-effects library has been folded into elm-lang/core. In 0.17, Effects is replaced by Cmd (Commands), which live in Platform.Cmd.

    Example Transformation:

    -- 0.16
    update : Action -> Model -> (Model, Effects Action)
    update action model = ...
    
    -- 0.17
    update : Msg -> Model -> (Model, Cmd Msg)
    update msg model = ...

    Note: Cmd is typically imported as import Platform.Cmd as Cmd exposing (Cmd).

    update : Msg -> Model -> (Model, Cmd Msg)
    update msg model =
      case msg of
        RequestMore ->
          ( model, getRandomGif model.topic )
    
        NewGif maybeUrl ->
          ( Model model.topic (Maybe.withDefault model.gifUrl maybeUrl)
          , Cmd.none
          )
  6. Remove Signal.Address from views

    main

    In 0.17, Signal.Address has been removed. HTML nodes now specify the type of messages they produce using Html Msg.

    Key Changes:

    • Remove address arguments from view functions.
    • Change type signature from view : Signal.Address Action -> Model -> Html to view : Model -> Html Msg.
    • Replace Signal.forwardTo address Top with map Top (or the relevant constructor) to wrap child views.

    Example Transformation:

    -- 0.16
    view : Signal.Address Action -> Model -> Html
    view address model =
      div []
        [ Counter.view (Signal.forwardTo address Top) model.topCounter
        ]
    
    -- 0.17
    view : Model -> Html Msg
    view model =
      div []
        [ map Top (Counter.view model.topCounter)
        ]
    view : Model -> Html Msg
    view model =
      div []
        [ map Top (Counter.view model.topCounter)
        , button [ onClick Reset ] [ text "RESET" ]
        ]
  7. Configure `elm.json` for packages

    main

    When creating an Elm package, you must use an elm.json file with the `

    {
        "type": "package",
        "name": "elm/json",
        "summary": "Encode and decode JSON values",
        "license": "BSD-3-Clause",
        "version": "1.0.0",
        "exposed-modules": [
            "Json.Decode",
            "Json.Encode"
        ],
        "elm-version": "0.19.0 <= v < 0.20.0",
        "dependencies": {
            "elm/core": "1.0.0 <= v < 2.0.0"
        },
        "test-dependencies": {}
    }
  8. Hide implementation details in packages using an Internal module

    main

    When developing a package where multiple internal modules need access to a core type, but you want to keep that type's implementation hidden from end-users, use an .Internal module pattern:

    1. Define the core type in a module like Parser.Internal.
    2. Have all internal modules import Parser.Internal to access the type.
    3. In your public-facing module (e.g., Parser), import the internal module and re-export the type as a type alias.

    This allows your package modules to work with the actual type while users only see the clean, public API.

    -- 1. Define in the internal module
    module Parser.Internal exposing (..)
    
    type Parser a = Parser ...
    
    -- 2. In the public module, re-export it
    module Parser exposing (..)
    
    import Parser.Internal as Internal
    
    type alias Parser a = 
      Internal.Parser a
  9. Run the Elm binary from node_modules

    main

    After installing Elm via npm in your project, the binary is located in ./node_modules/.bin/elm. You can use this path to check the version or compile your Elm files without affecting global installations.

    To verify the installation:

    ./node_modules/.bin/elm --version

    To compile an Elm file:

    ./node_modules/.bin/elm make src/Main.elm