├── .github
├── CONTRIBUTING.md
├── ISSUE_TEMPLATE.md
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── set-issue-expectations.yml
│ └── set-pull-expectations.yml
├── .gitignore
├── .travis.yml
├── LICENSE
├── README.md
├── builder
└── src
│ ├── BackgroundWriter.hs
│ ├── Build.hs
│ ├── Deps
│ ├── Bump.hs
│ ├── Diff.hs
│ ├── Registry.hs
│ ├── Solver.hs
│ └── Website.hs
│ ├── Elm
│ ├── Details.hs
│ └── Outline.hs
│ ├── File.hs
│ ├── Generate.hs
│ ├── Http.hs
│ ├── Reporting.hs
│ ├── Reporting
│ ├── Exit.hs
│ ├── Exit
│ │ └── Help.hs
│ └── Task.hs
│ └── Stuff.hs
├── cabal.config
├── compiler
└── src
│ ├── AST
│ ├── Canonical.hs
│ ├── Optimized.hs
│ ├── Source.hs
│ └── Utils
│ │ ├── Binop.hs
│ │ ├── Shader.hs
│ │ └── Type.hs
│ ├── Canonicalize
│ ├── Effects.hs
│ ├── Environment.hs
│ ├── Environment
│ │ ├── Dups.hs
│ │ ├── Foreign.hs
│ │ └── Local.hs
│ ├── Expression.hs
│ ├── Module.hs
│ ├── Pattern.hs
│ └── Type.hs
│ ├── Compile.hs
│ ├── Data
│ ├── Bag.hs
│ ├── Index.hs
│ ├── Map
│ │ └── Utils.hs
│ ├── Name.hs
│ ├── NonEmptyList.hs
│ ├── OneOrMore.hs
│ └── Utf8.hs
│ ├── Elm
│ ├── Compiler
│ │ ├── Imports.hs
│ │ ├── Type.hs
│ │ └── Type
│ │ │ └── Extract.hs
│ ├── Constraint.hs
│ ├── Docs.hs
│ ├── Float.hs
│ ├── Interface.hs
│ ├── Kernel.hs
│ ├── Licenses.hs
│ ├── Magnitude.hs
│ ├── ModuleName.hs
│ ├── Package.hs
│ ├── String.hs
│ └── Version.hs
│ ├── Generate
│ ├── Html.hs
│ ├── JavaScript.hs
│ ├── JavaScript
│ │ ├── Builder.hs
│ │ ├── Expression.hs
│ │ ├── Functions.hs
│ │ └── Name.hs
│ └── Mode.hs
│ ├── Json
│ ├── Decode.hs
│ ├── Encode.hs
│ └── String.hs
│ ├── Nitpick
│ ├── Debug.hs
│ └── PatternMatches.hs
│ ├── Optimize
│ ├── Case.hs
│ ├── DecisionTree.hs
│ ├── Expression.hs
│ ├── Module.hs
│ ├── Names.hs
│ └── Port.hs
│ ├── Parse
│ ├── Declaration.hs
│ ├── Expression.hs
│ ├── Keyword.hs
│ ├── Module.hs
│ ├── Number.hs
│ ├── Pattern.hs
│ ├── Primitives.hs
│ ├── Shader.hs
│ ├── Space.hs
│ ├── String.hs
│ ├── Symbol.hs
│ ├── Type.hs
│ └── Variable.hs
│ ├── Reporting
│ ├── Annotation.hs
│ ├── Doc.hs
│ ├── Error.hs
│ ├── Error
│ │ ├── Canonicalize.hs
│ │ ├── Docs.hs
│ │ ├── Import.hs
│ │ ├── Json.hs
│ │ ├── Main.hs
│ │ ├── Pattern.hs
│ │ ├── Syntax.hs
│ │ └── Type.hs
│ ├── Render
│ │ ├── Code.hs
│ │ ├── Type.hs
│ │ └── Type
│ │ │ └── Localizer.hs
│ ├── Report.hs
│ ├── Result.hs
│ ├── Suggest.hs
│ └── Warning.hs
│ └── Type
│ ├── Constrain
│ ├── Expression.hs
│ ├── Module.hs
│ └── Pattern.hs
│ ├── Error.hs
│ ├── Instantiate.hs
│ ├── Occurs.hs
│ ├── Solve.hs
│ ├── Type.hs
│ ├── Unify.hs
│ └── UnionFind.hs
├── docs
├── elm.json
│ ├── application.md
│ └── package.md
└── upgrade-instructions
│ ├── 0.16.md
│ ├── 0.17.md
│ ├── 0.18.md
│ ├── 0.19.0.md
│ ├── 0.19.1.md
│ └── earlier.md
├── elm.cabal
├── hints
├── bad-recursion.md
├── comparing-custom-types.md
├── comparing-records.md
├── implicit-casts.md
├── import-cycles.md
├── imports.md
├── infinite-type.md
├── init.md
├── missing-patterns.md
├── optimize.md
├── port-modules.md
├── recursive-alias.md
├── repl.md
├── shadowing.md
├── tuples.md
└── type-annotations.md
├── installers
├── README.md
├── linux
│ ├── Dockerfile
│ └── README.md
├── mac
│ ├── Distribution.xml
│ ├── README.md
│ ├── Resources
│ │ └── en.lproj
│ │ │ ├── conclusion.rtf
│ │ │ └── welcome.rtf
│ ├── helper-scripts
│ │ ├── elm-startup.sh
│ │ └── uninstall.sh
│ ├── make-installer.sh
│ ├── postinstall
│ └── preinstall
├── npm
│ ├── .gitignore
│ ├── .npmignore
│ ├── PUBLISHING.md
│ ├── README.md
│ ├── bin
│ │ └── elm
│ ├── binary.js
│ ├── install.js
│ ├── package.json
│ ├── packages
│ │ ├── darwin_arm64
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ ├── darwin_x64
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ ├── linux_arm64
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ ├── linux_x64
│ │ │ ├── README.md
│ │ │ └── package.json
│ │ └── win32_x64
│ │ │ ├── README.md
│ │ │ └── package.json
│ └── troubleshooting.md
└── win
│ ├── CreateInternetShortcut.nsh
│ ├── Nsisfile.nsi
│ ├── README.md
│ ├── inst.dat
│ ├── logo.ico
│ ├── make_installer.cmd
│ ├── removefrompath.vbs
│ ├── uninst.dat
│ ├── updatepath.vbs
│ └── welcome.bmp
├── reactor
├── assets
│ ├── favicon.ico
│ ├── source-code-pro.ttf
│ ├── source-sans-pro.ttf
│ └── styles.css
├── check.py
├── elm.json
└── src
│ ├── Deps.elm
│ ├── Errors.elm
│ ├── Index.elm
│ ├── Index
│ ├── Icon.elm
│ ├── Navigator.elm
│ └── Skeleton.elm
│ ├── NotFound.elm
│ └── mock.txt
├── roadmap.md
├── terminal
├── impl
│ ├── Terminal.hs
│ └── Terminal
│ │ ├── Chomp.hs
│ │ ├── Error.hs
│ │ ├── Helpers.hs
│ │ └── Internal.hs
└── src
│ ├── Bump.hs
│ ├── Develop.hs
│ ├── Develop
│ ├── Generate
│ │ ├── Help.hs
│ │ └── Index.hs
│ ├── Socket.hs
│ ├── StaticFiles.hs
│ └── StaticFiles
│ │ └── Build.hs
│ ├── Diff.hs
│ ├── Init.hs
│ ├── Install.hs
│ ├── Main.hs
│ ├── Make.hs
│ ├── Publish.hs
│ └── Repl.hs
└── worker
├── elm.cabal
├── elm.json
├── nginx.conf
├── outlines
├── compile
│ └── elm.json
└── repl
│ └── elm.json
└── src
├── Artifacts.hs
├── Cors.hs
├── Endpoint
├── Compile.hs
├── Quotes.hs
├── Repl.hs
└── Slack.hs
├── Errors.elm
└── Main.hs
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Elm
2 |
3 | Thanks helping with the development of Elm! This document describes the basic
4 | standards for opening pull requests and making the review process as smooth as
5 | possible.
6 |
7 |
8 | ## Expectations
9 |
10 | - Pull requests are reviewed in [batches](https://github.com/elm/expectations/blob/master/batching.md), so it can take some time to get a response.
11 | - Smaller pull requests are easier to review. To fix nine typos, nine specific issues will always go faster than one big one. Learn why [here](https://github.com/elm/expectations/blob/master/small-pull-requests.md).
12 | - Reviewers may not know as much as you about certain situations, so add links to supporting evidence for important claims, especially regarding standards for CSS, HTTP, URI, etc.
13 |
14 | Finally, please be patient with the core team. They are trying their best with limited resources!
15 |
16 |
17 | ## Style Guide
18 |
19 | * Haskell — conform to [these guidelines][haskell]
20 | * JavaScript — use [Google's JS style guide][js]
21 |
22 | [haskell]: https://gist.github.com/evancz/0a1f3717c92fe71702be
23 | [js]: https://google.github.io/styleguide/javascriptguide.xml
24 |
25 |
26 | ## Branches
27 |
28 | [The master branch][master] is the home of the next release of the compiler
29 | so new features and improvements get merged there. Most pull requests
30 | should target this branch!
31 |
32 | [master]: http://github.com/elm-lang/elm/tree/master
33 |
34 |
35 | ## Licensing
36 |
37 | Nothing extra to do for this. The default on GitHub described [here](https://docs.github.com/en/github/site-policy/github-terms-of-service#6-contributions-under-repository-license) is that:
38 |
39 | > Whenever you add Content to a repository containing notice of a license, you license that Content under the same terms, and you agree that you have the right to license that Content under those terms. If you have a separate agreement to license that Content under different terms, such as a contributor license agreement, that agreement will supersede.
40 |
41 |
42 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 | **Quick Summary:** ???
3 |
4 |
5 | ## SSCCE
6 |
7 | ```elm
8 |
9 | ```
10 |
11 | - **Elm:** ???
12 | - **Browser:** ???
13 | - **Operating System:** ???
14 |
15 |
16 | ## Additional Details
17 |
18 | ???
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
2 | **Quick Summary:** ???
3 |
4 |
5 | ## SSCCE
6 |
7 | ```elm
8 |
9 | ```
10 |
11 | - **Elm:** ???
12 | - **Browser:** ???
13 | - **Operating System:** ???
14 |
15 |
16 | ## Additional Details
17 |
18 | ???
19 |
--------------------------------------------------------------------------------
/.github/workflows/set-issue-expectations.yml:
--------------------------------------------------------------------------------
1 | name: Set Issue Expectations
2 | on:
3 | issues:
4 | types: [opened]
5 | jobs:
6 | comment-on-issue:
7 | name: Comment On Issue
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/github@v1.0.0
11 | env:
12 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
13 | with:
14 | args: |
15 | comment "Thanks for reporting this! To set expectations:
16 |
17 | - Issues are reviewed in [batches](https://github.com/elm/expectations/blob/master/batching.md), so it can take some time to get a response.
18 | - Ask questions in a [community forum](https://elm-lang.org/community). You will get an answer quicker that way!
19 | - If you experience something similar, open a new issue. [We like duplicates](https://github.com/elm/expectations/blob/master/duplicates.md).
20 |
21 | Finally, please be patient with the core team. They are trying their best with limited resources."
22 |
--------------------------------------------------------------------------------
/.github/workflows/set-pull-expectations.yml:
--------------------------------------------------------------------------------
1 | on:
2 | pull_request_target:
3 | types: [opened]
4 |
5 | jobs:
6 | comment-on-pull:
7 | name: Comment On Pull
8 | runs-on: ubuntu-latest
9 | steps:
10 | - uses: actions/github-script@v3
11 | with:
12 | github-token: ${{secrets.GITHUB_TOKEN}}
13 | script: |
14 | github.issues.createComment({
15 | issue_number: context.issue.number,
16 | owner: context.repo.owner,
17 | repo: context.repo.repo,
18 | body: "Thanks for suggesting these code changes. To set expectations:\n\n- Pull requests are reviewed in [batches](https://github.com/elm/expectations/blob/master/batching.md), so it can take some time to get a response.\n- Smaller pull requests are easier to review. To fix nine typos, nine specific issues will always go faster than one big one. Learn why [here](https://github.com/elm/expectations/blob/master/small-pull-requests.md).\n- Reviewers may not know as much as you about certain situations, so add links to supporting evidence for important claims, especially regarding standards for CSS, HTTP, URI, etc.\n\nFinally, please be patient with the core team. They are trying their best with limited resources."
19 | })
20 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | elm-stuff
2 | dist
3 | cabal-dev
4 | .cabal-sandbox/
5 | cabal.sandbox.config
6 | .DS_Store
7 | *~
8 | travis.log
9 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: minimal
2 | services: docker
3 |
4 | env:
5 | global:
6 | - LINUX_ARCHIVE=binary-for-linux-64-bit.gz
7 |
8 | before_install:
9 | - docker build -t elm -f installers/linux/Dockerfile .
10 | - docker cp $(docker create elm):/usr/local/bin/elm .
11 | - gzip -9 -c elm > $LINUX_ARCHIVE
12 |
13 | deploy:
14 | provider: releases
15 | api_key:
16 | secure: Yz2Lo4u9rZQ7Ee7ohAsrZpkqsYDUerCSMdSQIH8ryrf7phHhiloPEkTKsM+NupHqU/LEAVsunxbau4QrCEjA2vPavAPVk8cKomRUWK/YjbXHKa24hPkal2c+A2bnMQ6w3qYk/PjL9rW+Goq++/SNLcYZwHBV0Chl2blivMwWCSA=
17 | file: $LINUX_ARCHIVE
18 | skip_cleanup: true
19 | on:
20 | branch: master
21 | tags: true
22 |
23 | notifications:
24 | email:
25 | recipients:
26 | - rlefevre@dmy.fr
27 | on_success: change
28 | on_failure: change
29 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright 2012-present Evan Czaplicki
2 |
3 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
4 |
5 | 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
6 |
7 | 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
8 |
9 | 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
10 |
11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Elm
2 |
3 | A delightful language for reliable webapps.
4 |
5 | Check out the [Home Page](http://elm-lang.org/), [Try Online](http://elm-lang.org/try), or [The Official Guide](http://guide.elm-lang.org/)
6 |
7 |
8 |
9 |
10 | ## Install
11 |
12 | ✨ [Install](https://guide.elm-lang.org/install/elm.html) ✨
13 |
14 | For multiple versions, previous versions, and uninstallation, see the instructions [here](https://github.com/elm/compiler/blob/master/installers/README.md).
15 |
16 |
17 |
18 | ## Help
19 |
20 | If you are stuck, ask around on [the Elm slack channel][slack]. Folks are friendly and happy to help with questions!
21 |
22 | [slack]: http://elmlang.herokuapp.com/
23 |
--------------------------------------------------------------------------------
/builder/src/BackgroundWriter.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE BangPatterns #-}
2 | module BackgroundWriter
3 | ( Scope
4 | , withScope
5 | , writeBinary
6 | )
7 | where
8 |
9 |
10 | import Control.Concurrent (forkIO)
11 | import Control.Concurrent.MVar (MVar, newEmptyMVar, newMVar, putMVar, takeMVar)
12 | import qualified Data.Binary as Binary
13 | import Data.Foldable (traverse_)
14 |
15 | import qualified File
16 |
17 |
18 |
19 | -- BACKGROUND WRITER
20 |
21 |
22 | newtype Scope =
23 | Scope (MVar [MVar ()])
24 |
25 |
26 | withScope :: (Scope -> IO a) -> IO a
27 | withScope callback =
28 | do workList <- newMVar []
29 | result <- callback (Scope workList)
30 | mvars <- takeMVar workList
31 | traverse_ takeMVar mvars
32 | return result
33 |
34 |
35 | writeBinary :: (Binary.Binary a) => Scope -> FilePath -> a -> IO ()
36 | writeBinary (Scope workList) path value =
37 | do mvar <- newEmptyMVar
38 | _ <- forkIO (File.writeBinary path value >> putMVar mvar ())
39 | oldWork <- takeMVar workList
40 | let !newWork = mvar:oldWork
41 | putMVar workList newWork
42 |
43 |
--------------------------------------------------------------------------------
/builder/src/Deps/Bump.hs:
--------------------------------------------------------------------------------
1 | module Deps.Bump
2 | ( getPossibilities
3 | )
4 | where
5 |
6 |
7 | import qualified Data.List as List
8 |
9 | import qualified Deps.Registry as Registry
10 | import qualified Elm.Magnitude as M
11 | import qualified Elm.Version as V
12 |
13 |
14 |
15 | -- GET POSSIBILITIES
16 |
17 |
18 | getPossibilities :: Registry.KnownVersions -> [(V.Version, V.Version, M.Magnitude)]
19 | getPossibilities (Registry.KnownVersions latest previous) =
20 | let
21 | allVersions = reverse (latest:previous)
22 | minorPoints = map last (List.groupBy sameMajor allVersions)
23 | patchPoints = map last (List.groupBy sameMinor allVersions)
24 | in
25 | (latest, V.bumpMajor latest, M.MAJOR)
26 | : map (\v -> (v, V.bumpMinor v, M.MINOR)) minorPoints
27 | ++ map (\v -> (v, V.bumpPatch v, M.PATCH)) patchPoints
28 |
29 |
30 | sameMajor :: V.Version -> V.Version -> Bool
31 | sameMajor (V.Version major1 _ _) (V.Version major2 _ _) =
32 | major1 == major2
33 |
34 |
35 | sameMinor :: V.Version -> V.Version -> Bool
36 | sameMinor (V.Version major1 minor1 _) (V.Version major2 minor2 _) =
37 | major1 == major2 && minor1 == minor2
38 |
--------------------------------------------------------------------------------
/builder/src/Deps/Website.hs:
--------------------------------------------------------------------------------
1 | module Deps.Website
2 | ( domain
3 | , route
4 | , metadata
5 | )
6 | where
7 |
8 |
9 | import qualified Elm.Package as Pkg
10 | import qualified Elm.Version as V
11 | import qualified Http
12 |
13 |
14 | domain :: String
15 | domain =
16 | "https://package.elm-lang.org"
17 |
18 |
19 | route :: String -> [(String,String)] -> String
20 | route path params =
21 | Http.toUrl (domain ++ path) params
22 |
23 |
24 | metadata :: Pkg.Name -> V.Version -> String -> String
25 | metadata name version file =
26 | domain ++ "/packages/" ++ Pkg.toUrl name ++ "/" ++ V.toChars version ++ "/" ++ file
27 |
--------------------------------------------------------------------------------
/builder/src/Reporting/Exit/Help.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Reporting.Exit.Help
3 | ( Report
4 | , report
5 | , docReport
6 | , jsonReport
7 | , compilerReport
8 | , reportToDoc
9 | , reportToJson
10 | , toString
11 | , toStdout
12 | , toStderr
13 | )
14 | where
15 |
16 |
17 | import GHC.IO.Handle (hIsTerminalDevice)
18 | import System.IO (Handle, hPutStr, stderr, stdout)
19 |
20 | import qualified Json.Encode as E
21 | import Json.Encode ((==>))
22 | import Reporting.Doc ((<+>))
23 | import qualified Reporting.Doc as D
24 | import qualified Reporting.Error as Error
25 |
26 |
27 |
28 | -- REPORT
29 |
30 |
31 | data Report
32 | = CompilerReport FilePath Error.Module [Error.Module]
33 | | Report
34 | { _title :: String
35 | , _path :: Maybe FilePath
36 | , _message :: D.Doc
37 | }
38 |
39 |
40 | report :: String -> Maybe FilePath -> String -> [D.Doc] -> Report
41 | report title path startString others =
42 | Report title path $ D.stack (D.reflow startString:others)
43 |
44 |
45 | docReport :: String -> Maybe FilePath -> D.Doc -> [D.Doc] -> Report
46 | docReport title path startDoc others =
47 | Report title path $ D.stack (startDoc:others)
48 |
49 |
50 | jsonReport :: String -> Maybe FilePath -> D.Doc -> Report
51 | jsonReport =
52 | Report
53 |
54 |
55 | compilerReport :: FilePath -> Error.Module -> [Error.Module] -> Report
56 | compilerReport =
57 | CompilerReport
58 |
59 |
60 |
61 | -- TO DOC
62 |
63 |
64 | reportToDoc :: Report -> D.Doc
65 | reportToDoc report_ =
66 | case report_ of
67 | CompilerReport root e es ->
68 | Error.toDoc root e es
69 |
70 | Report title maybePath message ->
71 | let
72 | makeDashes n =
73 | replicate (max 1 (80 - n)) '-'
74 |
75 | errorBarEnd =
76 | case maybePath of
77 | Nothing ->
78 | makeDashes (4 + length title)
79 |
80 | Just path ->
81 | makeDashes (5 + length title + length path) ++ " " ++ path
82 |
83 | errorBar =
84 | D.dullcyan $
85 | "--" <+> D.fromChars title <+> D.fromChars errorBarEnd
86 | in
87 | D.stack [errorBar, message, ""]
88 |
89 |
90 |
91 | -- TO JSON
92 |
93 |
94 | reportToJson :: Report -> E.Value
95 | reportToJson report_ =
96 | case report_ of
97 | CompilerReport _ e es ->
98 | E.object
99 | [ "type" ==> E.chars "compile-errors"
100 | , "errors" ==> E.list Error.toJson (e:es)
101 | ]
102 |
103 | Report title maybePath message ->
104 | E.object
105 | [ "type" ==> E.chars "error"
106 | , "path" ==> maybe E.null E.chars maybePath
107 | , "title" ==> E.chars title
108 | , "message" ==> D.encode message
109 | ]
110 |
111 |
112 |
113 | -- OUTPUT
114 |
115 |
116 | toString :: D.Doc -> String
117 | toString =
118 | D.toString
119 |
120 |
121 | toStdout :: D.Doc -> IO ()
122 | toStdout doc =
123 | toHandle stdout doc
124 |
125 |
126 | toStderr :: D.Doc -> IO ()
127 | toStderr doc =
128 | toHandle stderr doc
129 |
130 |
131 | toHandle :: Handle -> D.Doc -> IO ()
132 | toHandle handle doc =
133 | do isTerminal <- hIsTerminalDevice handle
134 | if isTerminal
135 | then D.toAnsi handle doc
136 | else hPutStr handle (toString doc)
137 |
--------------------------------------------------------------------------------
/builder/src/Reporting/Task.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE Rank2Types #-}
2 | module Reporting.Task
3 | ( Task
4 | , run
5 | , throw
6 | , mapError
7 | --
8 | , io
9 | , mio
10 | , eio
11 | )
12 | where
13 |
14 |
15 |
16 | -- TASKS
17 |
18 |
19 | newtype Task x a =
20 | Task
21 | (
22 | forall result. (a -> IO result) -> (x -> IO result) -> IO result
23 | )
24 |
25 |
26 | run :: Task x a -> IO (Either x a)
27 | run (Task task) =
28 | task (return . Right) (return . Left)
29 |
30 |
31 | throw :: x -> Task x a
32 | throw x =
33 | Task $ \_ err -> err x
34 |
35 |
36 | mapError :: (x -> y) -> Task x a -> Task y a
37 | mapError func (Task task) =
38 | Task $ \ok err ->
39 | task ok (err . func)
40 |
41 |
42 |
43 | -- IO
44 |
45 |
46 | {-# INLINE io #-}
47 | io :: IO a -> Task x a
48 | io work =
49 | Task $ \ok _ -> work >>= ok
50 |
51 |
52 | mio :: x -> IO (Maybe a) -> Task x a
53 | mio x work =
54 | Task $ \ok err ->
55 | do result <- work
56 | case result of
57 | Just a -> ok a
58 | Nothing -> err x
59 |
60 |
61 | eio :: (x -> y) -> IO (Either x a) -> Task y a
62 | eio func work =
63 | Task $ \ok err ->
64 | do result <- work
65 | case result of
66 | Right a -> ok a
67 | Left x -> err (func x)
68 |
69 |
70 |
71 | -- INSTANCES
72 |
73 |
74 | instance Functor (Task x) where
75 | {-# INLINE fmap #-}
76 | fmap func (Task taskA) =
77 | Task $ \ok err ->
78 | let
79 | okA arg = ok (func arg)
80 | in
81 | taskA okA err
82 |
83 |
84 | instance Applicative (Task x) where
85 | {-# INLINE pure #-}
86 | pure a =
87 | Task $ \ok _ -> ok a
88 |
89 | {-# INLINE (<*>) #-}
90 | (<*>) (Task taskFunc) (Task taskArg) =
91 | Task $ \ok err ->
92 | let
93 | okFunc func =
94 | let
95 | okArg arg = ok (func arg)
96 | in
97 | taskArg okArg err
98 | in
99 | taskFunc okFunc err
100 |
101 |
102 | instance Monad (Task x) where
103 | {-# INLINE return #-}
104 | return = pure
105 |
106 | {-# INLINE (>>=) #-}
107 | (>>=) (Task taskA) callback =
108 | Task $ \ok err ->
109 | let
110 | okA a =
111 | case callback a of
112 | Task taskB -> taskB ok err
113 | in
114 | taskA okA err
115 |
--------------------------------------------------------------------------------
/builder/src/Stuff.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Stuff
3 | ( details
4 | , interfaces
5 | , objects
6 | , prepublishDir
7 | , elmi
8 | , elmo
9 | , temp
10 | , findRoot
11 | , withRootLock
12 | , withRegistryLock
13 | , PackageCache
14 | , getPackageCache
15 | , registry
16 | , package
17 | , getReplCache
18 | , getElmHome
19 | )
20 | where
21 |
22 |
23 | import qualified System.Directory as Dir
24 | import qualified System.Environment as Env
25 | import qualified System.FileLock as Lock
26 | import qualified System.FilePath as FP
27 | import System.FilePath ((>), (<.>))
28 |
29 | import qualified Elm.ModuleName as ModuleName
30 | import qualified Elm.Package as Pkg
31 | import qualified Elm.Version as V
32 |
33 |
34 |
35 | -- PATHS
36 |
37 |
38 | stuff :: FilePath -> FilePath
39 | stuff root =
40 | root > "elm-stuff" > compilerVersion
41 |
42 |
43 | details :: FilePath -> FilePath
44 | details root =
45 | stuff root > "d.dat"
46 |
47 |
48 | interfaces :: FilePath -> FilePath
49 | interfaces root =
50 | stuff root > "i.dat"
51 |
52 |
53 | objects :: FilePath -> FilePath
54 | objects root =
55 | stuff root > "o.dat"
56 |
57 |
58 | prepublishDir :: FilePath -> FilePath
59 | prepublishDir root =
60 | stuff root > "prepublish"
61 |
62 |
63 | compilerVersion :: FilePath
64 | compilerVersion =
65 | V.toChars V.compiler
66 |
67 |
68 |
69 | -- ELMI and ELMO
70 |
71 |
72 | elmi :: FilePath -> ModuleName.Raw -> FilePath
73 | elmi root name =
74 | toArtifactPath root name "elmi"
75 |
76 |
77 | elmo :: FilePath -> ModuleName.Raw -> FilePath
78 | elmo root name =
79 | toArtifactPath root name "elmo"
80 |
81 |
82 | toArtifactPath :: FilePath -> ModuleName.Raw -> String -> FilePath
83 | toArtifactPath root name ext =
84 | stuff root > ModuleName.toHyphenPath name <.> ext
85 |
86 |
87 |
88 | -- TEMP
89 |
90 |
91 | temp :: FilePath -> String -> FilePath
92 | temp root ext =
93 | stuff root > "temp" <.> ext
94 |
95 |
96 |
97 | -- ROOT
98 |
99 |
100 | findRoot :: IO (Maybe FilePath)
101 | findRoot =
102 | do dir <- Dir.getCurrentDirectory
103 | findRootHelp (FP.splitDirectories dir)
104 |
105 |
106 | findRootHelp :: [String] -> IO (Maybe FilePath)
107 | findRootHelp dirs =
108 | case dirs of
109 | [] ->
110 | return Nothing
111 |
112 | _:_ ->
113 | do exists <- Dir.doesFileExist (FP.joinPath dirs > "elm.json")
114 | if exists
115 | then return (Just (FP.joinPath dirs))
116 | else findRootHelp (init dirs)
117 |
118 |
119 |
120 | -- LOCKS
121 |
122 |
123 | withRootLock :: FilePath -> IO a -> IO a
124 | withRootLock root work =
125 | do let dir = stuff root
126 | Dir.createDirectoryIfMissing True dir
127 | Lock.withFileLock (dir > "lock") Lock.Exclusive (\_ -> work)
128 |
129 |
130 | withRegistryLock :: PackageCache -> IO a -> IO a
131 | withRegistryLock (PackageCache dir) work =
132 | Lock.withFileLock (dir > "lock") Lock.Exclusive (\_ -> work)
133 |
134 |
135 |
136 | -- PACKAGE CACHES
137 |
138 |
139 | newtype PackageCache = PackageCache FilePath
140 |
141 |
142 | getPackageCache :: IO PackageCache
143 | getPackageCache =
144 | PackageCache <$> getCacheDir "packages"
145 |
146 |
147 | registry :: PackageCache -> FilePath
148 | registry (PackageCache dir) =
149 | dir > "registry.dat"
150 |
151 |
152 | package :: PackageCache -> Pkg.Name -> V.Version -> FilePath
153 | package (PackageCache dir) name version =
154 | dir > Pkg.toFilePath name > V.toChars version
155 |
156 |
157 |
158 | -- CACHE
159 |
160 |
161 | getReplCache :: IO FilePath
162 | getReplCache =
163 | getCacheDir "repl"
164 |
165 |
166 | getCacheDir :: FilePath -> IO FilePath
167 | getCacheDir projectName =
168 | do home <- getElmHome
169 | let root = home > compilerVersion > projectName
170 | Dir.createDirectoryIfMissing True root
171 | return root
172 |
173 |
174 | getElmHome :: IO FilePath
175 | getElmHome =
176 | do maybeCustomHome <- Env.lookupEnv "ELM_HOME"
177 | case maybeCustomHome of
178 | Just customHome -> return customHome
179 | Nothing -> Dir.getAppUserDataDirectory "elm"
180 |
--------------------------------------------------------------------------------
/cabal.config:
--------------------------------------------------------------------------------
1 | profiling: False
2 | library-profiling: True
3 |
--------------------------------------------------------------------------------
/compiler/src/AST/Source.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module AST.Source
3 | ( Expr, Expr_(..), VarType(..)
4 | , Def(..)
5 | , Pattern, Pattern_(..)
6 | , Type, Type_(..)
7 | , Module(..)
8 | , getName
9 | , getImportName
10 | , Import(..)
11 | , Value(..)
12 | , Union(..)
13 | , Alias(..)
14 | , Infix(..)
15 | , Port(..)
16 | , Effects(..)
17 | , Manager(..)
18 | , Docs(..)
19 | , Comment(..)
20 | , Exposing(..)
21 | , Exposed(..)
22 | , Privacy(..)
23 | )
24 | where
25 |
26 |
27 | import Data.Name (Name)
28 | import qualified Data.Name as Name
29 |
30 | import qualified AST.Utils.Binop as Binop
31 | import qualified AST.Utils.Shader as Shader
32 | import qualified Elm.Float as EF
33 | import qualified Elm.String as ES
34 | import qualified Parse.Primitives as P
35 | import qualified Reporting.Annotation as A
36 |
37 |
38 |
39 | -- EXPRESSIONS
40 |
41 |
42 | type Expr = A.Located Expr_
43 |
44 |
45 | data Expr_
46 | = Chr ES.String
47 | | Str ES.String
48 | | Int Int
49 | | Float EF.Float
50 | | Var VarType Name
51 | | VarQual VarType Name Name
52 | | List [Expr]
53 | | Op Name
54 | | Negate Expr
55 | | Binops [(Expr, A.Located Name)] Expr
56 | | Lambda [Pattern] Expr
57 | | Call Expr [Expr]
58 | | If [(Expr, Expr)] Expr
59 | | Let [A.Located Def] Expr
60 | | Case Expr [(Pattern, Expr)]
61 | | Accessor Name
62 | | Access Expr (A.Located Name)
63 | | Update (A.Located Name) [(A.Located Name, Expr)]
64 | | Record [(A.Located Name, Expr)]
65 | | Unit
66 | | Tuple Expr Expr [Expr]
67 | | Shader Shader.Source Shader.Types
68 |
69 |
70 | data VarType = LowVar | CapVar
71 |
72 |
73 |
74 | -- DEFINITIONS
75 |
76 |
77 | data Def
78 | = Define (A.Located Name) [Pattern] Expr (Maybe Type)
79 | | Destruct Pattern Expr
80 |
81 |
82 |
83 | -- PATTERN
84 |
85 |
86 | type Pattern = A.Located Pattern_
87 |
88 |
89 | data Pattern_
90 | = PAnything
91 | | PVar Name
92 | | PRecord [A.Located Name]
93 | | PAlias Pattern (A.Located Name)
94 | | PUnit
95 | | PTuple Pattern Pattern [Pattern]
96 | | PCtor A.Region Name [Pattern]
97 | | PCtorQual A.Region Name Name [Pattern]
98 | | PList [Pattern]
99 | | PCons Pattern Pattern
100 | | PChr ES.String
101 | | PStr ES.String
102 | | PInt Int
103 |
104 |
105 |
106 | -- TYPE
107 |
108 |
109 | type Type =
110 | A.Located Type_
111 |
112 |
113 | data Type_
114 | = TLambda Type Type
115 | | TVar Name
116 | | TType A.Region Name [Type]
117 | | TTypeQual A.Region Name Name [Type]
118 | | TRecord [(A.Located Name, Type)] (Maybe (A.Located Name))
119 | | TUnit
120 | | TTuple Type Type [Type]
121 |
122 |
123 |
124 | -- MODULE
125 |
126 |
127 | data Module =
128 | Module
129 | { _name :: Maybe (A.Located Name)
130 | , _exports :: A.Located Exposing
131 | , _docs :: Docs
132 | , _imports :: [Import]
133 | , _values :: [A.Located Value]
134 | , _unions :: [A.Located Union]
135 | , _aliases :: [A.Located Alias]
136 | , _binops :: [A.Located Infix]
137 | , _effects :: Effects
138 | }
139 |
140 |
141 | getName :: Module -> Name
142 | getName (Module maybeName _ _ _ _ _ _ _ _) =
143 | case maybeName of
144 | Just (A.At _ name) ->
145 | name
146 |
147 | Nothing ->
148 | Name._Main
149 |
150 |
151 | getImportName :: Import -> Name
152 | getImportName (Import (A.At _ name) _ _) =
153 | name
154 |
155 |
156 | data Import =
157 | Import
158 | { _import :: A.Located Name
159 | , _alias :: Maybe Name
160 | , _exposing :: Exposing
161 | }
162 |
163 |
164 | data Value = Value (A.Located Name) [Pattern] Expr (Maybe Type)
165 | data Union = Union (A.Located Name) [A.Located Name] [(A.Located Name, [Type])]
166 | data Alias = Alias (A.Located Name) [A.Located Name] Type
167 | data Infix = Infix Name Binop.Associativity Binop.Precedence Name
168 | data Port = Port (A.Located Name) Type
169 |
170 |
171 | data Effects
172 | = NoEffects
173 | | Ports [Port]
174 | | Manager A.Region Manager
175 |
176 |
177 | data Manager
178 | = Cmd (A.Located Name)
179 | | Sub (A.Located Name)
180 | | Fx (A.Located Name) (A.Located Name)
181 |
182 |
183 | data Docs
184 | = NoDocs A.Region
185 | | YesDocs Comment [(Name, Comment)]
186 |
187 |
188 | newtype Comment =
189 | Comment P.Snippet
190 |
191 |
192 |
193 | -- EXPOSING
194 |
195 |
196 | data Exposing
197 | = Open
198 | | Explicit [Exposed]
199 |
200 |
201 | data Exposed
202 | = Lower (A.Located Name)
203 | | Upper (A.Located Name) Privacy
204 | | Operator A.Region Name
205 |
206 |
207 | data Privacy
208 | = Public A.Region
209 | | Private
210 |
--------------------------------------------------------------------------------
/compiler/src/AST/Utils/Binop.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module AST.Utils.Binop
3 | ( Precedence(..)
4 | , Associativity(..)
5 | )
6 | where
7 |
8 |
9 | import Prelude hiding (Either(..))
10 | import Control.Monad (liftM)
11 | import Data.Binary
12 |
13 |
14 |
15 | -- BINOP STUFF
16 |
17 |
18 | newtype Precedence = Precedence Int
19 | deriving (Eq, Ord)
20 |
21 |
22 | data Associativity
23 | = Left
24 | | Non
25 | | Right
26 | deriving (Eq)
27 |
28 |
29 |
30 | -- BINARY
31 |
32 |
33 | instance Binary Precedence where
34 | get =
35 | liftM Precedence get
36 |
37 | put (Precedence n) =
38 | put n
39 |
40 |
41 | instance Binary Associativity where
42 | get =
43 | do n <- getWord8
44 | case n of
45 | 0 -> return Left
46 | 1 -> return Non
47 | 2 -> return Right
48 | _ -> fail "Error reading valid associativity from serialized string"
49 |
50 | put assoc =
51 | putWord8 $
52 | case assoc of
53 | Left -> 0
54 | Non -> 1
55 | Right -> 2
56 |
--------------------------------------------------------------------------------
/compiler/src/AST/Utils/Shader.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE EmptyDataDecls #-}
3 | module AST.Utils.Shader
4 | ( Source
5 | , Types(..)
6 | , Type(..)
7 | , fromChars
8 | , toJsStringBuilder
9 | )
10 | where
11 |
12 |
13 | import Control.Monad (liftM)
14 | import Data.Binary (Binary, get, put)
15 | import qualified Data.ByteString as BS
16 | import qualified Data.ByteString.Builder as B
17 | import qualified Data.ByteString.UTF8 as BS_UTF8
18 | import qualified Data.Map as Map
19 | import qualified Data.Name as Name
20 |
21 |
22 |
23 | -- SOURCE
24 |
25 |
26 | newtype Source =
27 | Source BS.ByteString
28 |
29 |
30 |
31 | -- TYPES
32 |
33 |
34 | data Types =
35 | Types
36 | { _attribute :: Map.Map Name.Name Type
37 | , _uniform :: Map.Map Name.Name Type
38 | , _varying :: Map.Map Name.Name Type
39 | }
40 |
41 |
42 | data Type
43 | = Int
44 | | Float
45 | | V2
46 | | V3
47 | | V4
48 | | M4
49 | | Texture
50 |
51 |
52 |
53 | -- TO BUILDER
54 |
55 |
56 | toJsStringBuilder :: Source -> B.Builder
57 | toJsStringBuilder (Source src) =
58 | B.byteString src
59 |
60 |
61 |
62 | -- FROM CHARS
63 |
64 |
65 | fromChars :: [Char] -> Source
66 | fromChars chars =
67 | Source (BS_UTF8.fromString (escape chars))
68 |
69 |
70 | escape :: [Char] -> [Char]
71 | escape chars =
72 | case chars of
73 | [] ->
74 | []
75 |
76 | c:cs
77 | | c == '\r' -> escape cs
78 | | c == '\n' -> '\\' : 'n' : escape cs
79 | | c == '\"' -> '\\' : '"' : escape cs
80 | | c == '\'' -> '\\' : '\'' : escape cs
81 | | c == '\\' -> '\\' : '\\' : escape cs
82 | | otherwise -> c : escape cs
83 |
84 |
85 |
86 | -- BINARY
87 |
88 |
89 | instance Binary Source where
90 | get = liftM Source get
91 | put (Source a) = put a
92 |
--------------------------------------------------------------------------------
/compiler/src/AST/Utils/Type.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module AST.Utils.Type
3 | ( delambda
4 | , dealias
5 | , deepDealias
6 | , iteratedDealias
7 | )
8 | where
9 |
10 |
11 | import qualified Data.Map as Map
12 | import qualified Data.Name as Name
13 |
14 | import AST.Canonical (Type(..), AliasType(..), FieldType(..))
15 |
16 |
17 |
18 | -- DELAMBDA
19 |
20 |
21 | delambda :: Type -> [Type]
22 | delambda tipe =
23 | case tipe of
24 | TLambda arg result ->
25 | arg : delambda result
26 |
27 | _ ->
28 | [tipe]
29 |
30 |
31 |
32 | -- DEALIAS
33 |
34 |
35 | dealias :: [(Name.Name, Type)] -> AliasType -> Type
36 | dealias args aliasType =
37 | case aliasType of
38 | Holey tipe ->
39 | dealiasHelp (Map.fromList args) tipe
40 |
41 | Filled tipe ->
42 | tipe
43 |
44 |
45 | dealiasHelp :: Map.Map Name.Name Type -> Type -> Type
46 | dealiasHelp typeTable tipe =
47 | case tipe of
48 | TLambda a b ->
49 | TLambda
50 | (dealiasHelp typeTable a)
51 | (dealiasHelp typeTable b)
52 |
53 | TVar x ->
54 | Map.findWithDefault tipe x typeTable
55 |
56 | TRecord fields ext ->
57 | TRecord (Map.map (dealiasField typeTable) fields) ext
58 |
59 | TAlias home name args t' ->
60 | TAlias home name (map (fmap (dealiasHelp typeTable)) args) t'
61 |
62 | TType home name args ->
63 | TType home name (map (dealiasHelp typeTable) args)
64 |
65 | TUnit ->
66 | TUnit
67 |
68 | TTuple a b maybeC ->
69 | TTuple
70 | (dealiasHelp typeTable a)
71 | (dealiasHelp typeTable b)
72 | (fmap (dealiasHelp typeTable) maybeC)
73 |
74 |
75 | dealiasField :: Map.Map Name.Name Type -> FieldType -> FieldType
76 | dealiasField typeTable (FieldType index tipe) =
77 | FieldType index (dealiasHelp typeTable tipe)
78 |
79 |
80 |
81 | -- DEEP DEALIAS
82 |
83 |
84 | deepDealias :: Type -> Type
85 | deepDealias tipe =
86 | case tipe of
87 | TLambda a b ->
88 | TLambda (deepDealias a) (deepDealias b)
89 |
90 | TVar _ ->
91 | tipe
92 |
93 | TRecord fields ext ->
94 | TRecord (Map.map deepDealiasField fields) ext
95 |
96 | TAlias _ _ args tipe' ->
97 | deepDealias (dealias args tipe')
98 |
99 | TType home name args ->
100 | TType home name (map deepDealias args)
101 |
102 | TUnit ->
103 | TUnit
104 |
105 | TTuple a b c ->
106 | TTuple (deepDealias a) (deepDealias b) (fmap deepDealias c)
107 |
108 |
109 | deepDealiasField :: FieldType -> FieldType
110 | deepDealiasField (FieldType index tipe) =
111 | FieldType index (deepDealias tipe)
112 |
113 |
114 |
115 | -- ITERATED DEALIAS
116 |
117 |
118 | iteratedDealias :: Type -> Type
119 | iteratedDealias tipe =
120 | case tipe of
121 | TAlias _ _ args realType ->
122 | iteratedDealias (dealias args realType)
123 |
124 | _ ->
125 | tipe
126 |
--------------------------------------------------------------------------------
/compiler/src/Canonicalize/Environment/Dups.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Canonicalize.Environment.Dups
4 | ( detect
5 | , checkFields
6 | , checkFields'
7 | , Dict
8 | , none
9 | , one
10 | , insert
11 | , union
12 | , unions
13 | )
14 | where
15 |
16 |
17 | import qualified Data.Map as Map
18 | import qualified Data.Name as Name
19 |
20 | import qualified Data.OneOrMore as OneOrMore
21 | import qualified Reporting.Annotation as A
22 | import qualified Reporting.Error.Canonicalize as Error
23 | import qualified Reporting.Result as Result
24 |
25 |
26 |
27 | -- DUPLICATE TRACKER
28 |
29 |
30 | type Dict value =
31 | Map.Map Name.Name (OneOrMore.OneOrMore (Info value))
32 |
33 |
34 | data Info value =
35 | Info
36 | { _region :: A.Region
37 | , _value :: value
38 | }
39 |
40 |
41 |
42 | -- DETECT
43 |
44 |
45 | type ToError =
46 | Name.Name -> A.Region -> A.Region -> Error.Error
47 |
48 |
49 | detect :: ToError -> Dict a -> Result.Result i w Error.Error (Map.Map Name.Name a)
50 | detect toError dict =
51 | Map.traverseWithKey (detectHelp toError) dict
52 |
53 |
54 | detectHelp :: ToError -> Name.Name -> OneOrMore.OneOrMore (Info a) -> Result.Result i w Error.Error a
55 | detectHelp toError name values =
56 | case values of
57 | OneOrMore.One (Info _ value) ->
58 | return value
59 |
60 | OneOrMore.More left right ->
61 | let
62 | (Info r1 _, Info r2 _) =
63 | OneOrMore.getFirstTwo left right
64 | in
65 | Result.throw (toError name r1 r2)
66 |
67 |
68 |
69 | -- CHECK FIELDS
70 |
71 |
72 | checkFields :: [(A.Located Name.Name, a)] -> Result.Result i w Error.Error (Map.Map Name.Name a)
73 | checkFields fields =
74 | detect Error.DuplicateField (foldr addField none fields)
75 |
76 |
77 | addField :: (A.Located Name.Name, a) -> Dict a -> Dict a
78 | addField (A.At region name, value) dups =
79 | Map.insertWith OneOrMore.more name (OneOrMore.one (Info region value)) dups
80 |
81 |
82 | checkFields' :: (A.Region -> a -> b) -> [(A.Located Name.Name, a)] -> Result.Result i w Error.Error (Map.Map Name.Name b)
83 | checkFields' toValue fields =
84 | detect Error.DuplicateField (foldr (addField' toValue) none fields)
85 |
86 |
87 | addField' :: (A.Region -> a -> b) -> (A.Located Name.Name, a) -> Dict b -> Dict b
88 | addField' toValue (A.At region name, value) dups =
89 | Map.insertWith OneOrMore.more name (OneOrMore.one (Info region (toValue region value))) dups
90 |
91 |
92 |
93 | -- BUILDING DICTIONARIES
94 |
95 |
96 | none :: Dict a
97 | none =
98 | Map.empty
99 |
100 |
101 | one :: Name.Name -> A.Region -> value -> Dict value
102 | one name region value =
103 | Map.singleton name (OneOrMore.one (Info region value))
104 |
105 |
106 | insert :: Name.Name -> A.Region -> a -> Dict a -> Dict a
107 | insert name region value dict =
108 | Map.insertWith (\new old -> OneOrMore.more old new) name (OneOrMore.one (Info region value)) dict
109 |
110 |
111 | union :: Dict a -> Dict a -> Dict a
112 | union a b =
113 | Map.unionWith OneOrMore.more a b
114 |
115 |
116 | unions :: [Dict a] -> Dict a
117 | unions dicts =
118 | Map.unionsWith OneOrMore.more dicts
119 |
--------------------------------------------------------------------------------
/compiler/src/Compile.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall -fno-warn-unused-do-bind #-}
2 | module Compile
3 | ( Artifacts(..)
4 | , compile
5 | )
6 | where
7 |
8 |
9 | import qualified Data.Map as Map
10 | import qualified Data.Name as Name
11 |
12 | import qualified AST.Source as Src
13 | import qualified AST.Canonical as Can
14 | import qualified AST.Optimized as Opt
15 | import qualified Canonicalize.Module as Canonicalize
16 | import qualified Elm.Interface as I
17 | import qualified Elm.ModuleName as ModuleName
18 | import qualified Elm.Package as Pkg
19 | import qualified Nitpick.PatternMatches as PatternMatches
20 | import qualified Optimize.Module as Optimize
21 | import qualified Reporting.Error as E
22 | import qualified Reporting.Result as R
23 | import qualified Reporting.Render.Type.Localizer as Localizer
24 | import qualified Type.Constrain.Module as Type
25 | import qualified Type.Solve as Type
26 |
27 | import System.IO.Unsafe (unsafePerformIO)
28 |
29 |
30 |
31 | -- COMPILE
32 |
33 |
34 | data Artifacts =
35 | Artifacts
36 | { _modul :: Can.Module
37 | , _types :: Map.Map Name.Name Can.Annotation
38 | , _graph :: Opt.LocalGraph
39 | }
40 |
41 |
42 | compile :: Pkg.Name -> Map.Map ModuleName.Raw I.Interface -> Src.Module -> Either E.Error Artifacts
43 | compile pkg ifaces modul =
44 | do canonical <- canonicalize pkg ifaces modul
45 | annotations <- typeCheck modul canonical
46 | () <- nitpick canonical
47 | objects <- optimize modul annotations canonical
48 | return (Artifacts canonical annotations objects)
49 |
50 |
51 |
52 | -- PHASES
53 |
54 |
55 | canonicalize :: Pkg.Name -> Map.Map ModuleName.Raw I.Interface -> Src.Module -> Either E.Error Can.Module
56 | canonicalize pkg ifaces modul =
57 | case snd $ R.run $ Canonicalize.canonicalize pkg ifaces modul of
58 | Right canonical ->
59 | Right canonical
60 |
61 | Left errors ->
62 | Left $ E.BadNames errors
63 |
64 |
65 | typeCheck :: Src.Module -> Can.Module -> Either E.Error (Map.Map Name.Name Can.Annotation)
66 | typeCheck modul canonical =
67 | case unsafePerformIO (Type.run =<< Type.constrain canonical) of
68 | Right annotations ->
69 | Right annotations
70 |
71 | Left errors ->
72 | Left (E.BadTypes (Localizer.fromModule modul) errors)
73 |
74 |
75 | nitpick :: Can.Module -> Either E.Error ()
76 | nitpick canonical =
77 | case PatternMatches.check canonical of
78 | Right () ->
79 | Right ()
80 |
81 | Left errors ->
82 | Left (E.BadPatterns errors)
83 |
84 |
85 | optimize :: Src.Module -> Map.Map Name.Name Can.Annotation -> Can.Module -> Either E.Error Opt.LocalGraph
86 | optimize modul annotations canonical =
87 | case snd $ R.run $ Optimize.optimize annotations canonical of
88 | Right localGraph ->
89 | Right localGraph
90 |
91 | Left errors ->
92 | Left (E.BadMains (Localizer.fromModule modul) errors)
93 |
--------------------------------------------------------------------------------
/compiler/src/Data/Bag.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Data.Bag
3 | ( Bag(..)
4 | , empty
5 | , one
6 | , append
7 | , map
8 | , toList
9 | , fromList
10 | )
11 | where
12 |
13 |
14 | import Prelude hiding (map)
15 | import qualified Data.List as List
16 |
17 |
18 |
19 | -- BAGS
20 |
21 |
22 | data Bag a
23 | = Empty
24 | | One a
25 | | Two (Bag a) (Bag a)
26 |
27 |
28 |
29 | -- HELPERS
30 |
31 |
32 | empty :: Bag a
33 | empty =
34 | Empty
35 |
36 |
37 | one :: a -> Bag a
38 | one =
39 | One
40 |
41 |
42 | append :: Bag a -> Bag a -> Bag a
43 | append left right =
44 | case (left, right) of
45 | (other, Empty) ->
46 | other
47 |
48 | (Empty, other) ->
49 | other
50 |
51 | (_, _) ->
52 | Two left right
53 |
54 |
55 |
56 | -- MAP
57 |
58 |
59 | map :: (a -> b) -> Bag a -> Bag b
60 | map func bag =
61 | case bag of
62 | Empty ->
63 | Empty
64 |
65 | One a ->
66 | One (func a)
67 |
68 | Two left right ->
69 | Two (map func left) (map func right)
70 |
71 |
72 |
73 | -- TO LIST
74 |
75 |
76 | toList :: Bag a -> [a]
77 | toList bag =
78 | toListHelp bag []
79 |
80 |
81 | toListHelp :: Bag a -> [a] -> [a]
82 | toListHelp bag list =
83 | case bag of
84 | Empty ->
85 | list
86 |
87 | One x ->
88 | x : list
89 |
90 | Two a b ->
91 | toListHelp a (toListHelp b list)
92 |
93 |
94 |
95 | -- FROM LIST
96 |
97 |
98 | fromList :: (a -> b) -> [a] -> Bag b
99 | fromList func list =
100 | case list of
101 | [] ->
102 | Empty
103 |
104 | first : rest ->
105 | List.foldl' (add func) (One (func first)) rest
106 |
107 |
108 | add :: (a -> b) -> Bag b -> a -> Bag b
109 | add func bag value =
110 | Two (One (func value)) bag
111 |
--------------------------------------------------------------------------------
/compiler/src/Data/Index.hs:
--------------------------------------------------------------------------------
1 | module Data.Index
2 | ( ZeroBased
3 | , first
4 | , second
5 | , third
6 | , next
7 | , toMachine
8 | , toHuman
9 | , indexedMap
10 | , indexedTraverse
11 | , indexedForA
12 | , VerifiedList(..)
13 | , indexedZipWith
14 | , indexedZipWithA
15 | )
16 | where
17 |
18 |
19 | import Control.Monad (liftM)
20 | import Data.Binary
21 |
22 |
23 |
24 | -- ZERO BASED
25 |
26 |
27 | newtype ZeroBased = ZeroBased Int
28 | deriving (Eq, Ord)
29 |
30 |
31 | first :: ZeroBased
32 | first =
33 | ZeroBased 0
34 |
35 |
36 | second :: ZeroBased
37 | second =
38 | ZeroBased 1
39 |
40 |
41 | third :: ZeroBased
42 | third =
43 | ZeroBased 2
44 |
45 |
46 | {-# INLINE next #-}
47 | next :: ZeroBased -> ZeroBased
48 | next (ZeroBased i) =
49 | ZeroBased (i + 1)
50 |
51 |
52 |
53 | -- DESTRUCT
54 |
55 |
56 | toMachine :: ZeroBased -> Int
57 | toMachine (ZeroBased index) =
58 | index
59 |
60 |
61 | toHuman :: ZeroBased -> Int
62 | toHuman (ZeroBased index) =
63 | index + 1
64 |
65 |
66 |
67 | -- INDEXED MAP
68 |
69 |
70 | {-# INLINE indexedMap #-}
71 | indexedMap :: (ZeroBased -> a -> b) -> [a] -> [b]
72 | indexedMap func xs =
73 | zipWith func (map ZeroBased [0 .. length xs]) xs
74 |
75 |
76 | {-# INLINE indexedTraverse #-}
77 | indexedTraverse :: (Applicative f) => (ZeroBased -> a -> f b) -> [a] -> f [b]
78 | indexedTraverse func xs =
79 | sequenceA (indexedMap func xs)
80 |
81 |
82 | {-# INLINE indexedForA #-}
83 | indexedForA :: (Applicative f) => [a] -> (ZeroBased -> a -> f b) -> f [b]
84 | indexedForA xs func =
85 | sequenceA (indexedMap func xs)
86 |
87 |
88 |
89 | -- VERIFIED/INDEXED ZIP
90 |
91 |
92 | data VerifiedList a
93 | = LengthMatch [a]
94 | | LengthMismatch Int Int
95 |
96 |
97 | indexedZipWith :: (ZeroBased -> a -> b -> c) -> [a] -> [b] -> VerifiedList c
98 | indexedZipWith func listX listY =
99 | indexedZipWithHelp func 0 listX listY []
100 |
101 |
102 | indexedZipWithHelp :: (ZeroBased -> a -> b -> c) -> Int -> [a] -> [b] -> [c] -> VerifiedList c
103 | indexedZipWithHelp func index listX listY revListZ =
104 | case (listX, listY) of
105 | ([], []) ->
106 | LengthMatch (reverse revListZ)
107 |
108 | (x:xs, y:ys) ->
109 | indexedZipWithHelp func (index + 1) xs ys $
110 | func (ZeroBased index) x y : revListZ
111 |
112 | (_, _) ->
113 | LengthMismatch (index + length listX) (index + length listY)
114 |
115 |
116 | indexedZipWithA :: (Applicative f) => (ZeroBased -> a -> b -> f c) -> [a] -> [b] -> f (VerifiedList c)
117 | indexedZipWithA func listX listY =
118 | case indexedZipWith func listX listY of
119 | LengthMatch xs ->
120 | LengthMatch <$> sequenceA xs
121 |
122 | LengthMismatch x y ->
123 | pure (LengthMismatch x y)
124 |
125 |
126 |
127 | -- BINARY
128 |
129 |
130 | instance Binary ZeroBased where
131 | get = liftM ZeroBased get
132 | put (ZeroBased n) = put n
133 |
--------------------------------------------------------------------------------
/compiler/src/Data/Map/Utils.hs:
--------------------------------------------------------------------------------
1 | module Data.Map.Utils
2 | ( fromKeys
3 | , fromKeysA
4 | , fromValues
5 | , any
6 | )
7 | where
8 |
9 |
10 | import Prelude hiding (any)
11 | import qualified Data.Map as Map
12 | import Data.Map.Internal (Map(..))
13 |
14 |
15 |
16 | -- FROM KEYS
17 |
18 |
19 | fromKeys :: (Ord k) => (k -> v) -> [k] -> Map.Map k v
20 | fromKeys toValue keys =
21 | Map.fromList $ map (\k -> (k, toValue k)) keys
22 |
23 |
24 | fromKeysA :: (Applicative f, Ord k) => (k -> f v) -> [k] -> f (Map.Map k v)
25 | fromKeysA toValue keys =
26 | Map.fromList <$> traverse (\k -> (,) k <$> toValue k) keys
27 |
28 |
29 | fromValues :: (Ord k) => (v -> k) -> [v] -> Map.Map k v
30 | fromValues toKey values =
31 | Map.fromList $ map (\v -> (toKey v, v)) values
32 |
33 |
34 |
35 | -- ANY
36 |
37 |
38 | {-# INLINE any #-}
39 | any :: (v -> Bool) -> Map.Map k v -> Bool
40 | any isGood = go
41 | where
42 | go Tip = False
43 | go (Bin _ _ v l r) = isGood v || go l || go r
44 |
--------------------------------------------------------------------------------
/compiler/src/Data/NonEmptyList.hs:
--------------------------------------------------------------------------------
1 | module Data.NonEmptyList
2 | ( List(..)
3 | , singleton
4 | , toList
5 | , sortBy
6 | )
7 | where
8 |
9 |
10 | import Control.Monad (liftM2)
11 | import Data.Binary (Binary, get, put)
12 | import qualified Data.List as List
13 |
14 |
15 |
16 | -- LIST
17 |
18 |
19 | data List a =
20 | List a [a]
21 |
22 |
23 | singleton :: a -> List a
24 | singleton a =
25 | List a []
26 |
27 |
28 | toList :: List a -> [a]
29 | toList (List x xs) =
30 | x:xs
31 |
32 |
33 |
34 | -- INSTANCES
35 |
36 |
37 | instance Functor List where
38 | fmap func (List x xs) = List (func x) (map func xs)
39 |
40 |
41 | instance Traversable List where
42 | traverse func (List x xs) = List <$> func x <*> traverse func xs
43 |
44 |
45 | instance Foldable List where
46 | foldr step state (List x xs) = step x (foldr step state xs)
47 | foldl step state (List x xs) = foldl step (step state x) xs
48 | foldl1 step (List x xs) = foldl step x xs
49 |
50 |
51 |
52 | -- SORT BY
53 |
54 |
55 | sortBy :: (Ord b) => (a -> b) -> List a -> List a
56 | sortBy toRank (List x xs) =
57 | let
58 | comparison a b =
59 | compare (toRank a) (toRank b)
60 | in
61 | case List.sortBy comparison xs of
62 | [] ->
63 | List x []
64 |
65 | y:ys ->
66 | case comparison x y of
67 | LT -> List x (y:ys)
68 | EQ -> List x (y:ys)
69 | GT -> List y (List.insertBy comparison x ys)
70 |
71 |
72 |
73 | -- BINARY
74 |
75 |
76 | instance (Binary a) => Binary (List a) where
77 | put (List x xs) = put x >> put xs
78 | get = liftM2 List get get
79 |
--------------------------------------------------------------------------------
/compiler/src/Data/OneOrMore.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Data.OneOrMore
3 | ( OneOrMore(..)
4 | , one
5 | , more
6 | , map
7 | , destruct
8 | , getFirstTwo
9 | )
10 | where
11 |
12 |
13 | import Prelude hiding (map)
14 |
15 |
16 |
17 | -- ONE OR MORE
18 |
19 |
20 | data OneOrMore a
21 | = One a
22 | | More (OneOrMore a) (OneOrMore a)
23 |
24 |
25 | one :: a -> OneOrMore a
26 | one =
27 | One
28 |
29 |
30 | more :: OneOrMore a -> OneOrMore a -> OneOrMore a
31 | more =
32 | More
33 |
34 |
35 |
36 | -- MAP
37 |
38 |
39 | map :: (a -> b) -> OneOrMore a -> OneOrMore b
40 | map func oneOrMore =
41 | case oneOrMore of
42 | One value ->
43 | One (func value)
44 |
45 | More left right ->
46 | More (map func left) (map func right)
47 |
48 |
49 |
50 | -- DESTRUCT
51 |
52 |
53 | destruct :: (a -> [a] -> b) -> OneOrMore a -> b
54 | destruct func oneOrMore =
55 | destructLeft func oneOrMore []
56 |
57 |
58 | destructLeft :: (a -> [a] -> b) -> OneOrMore a -> [a] -> b
59 | destructLeft func oneOrMore xs =
60 | case oneOrMore of
61 | One x ->
62 | func x xs
63 |
64 | More a b ->
65 | destructLeft func a (destructRight b xs)
66 |
67 |
68 | destructRight :: OneOrMore a -> [a] -> [a]
69 | destructRight oneOrMore xs =
70 | case oneOrMore of
71 | One x ->
72 | x : xs
73 |
74 | More a b ->
75 | destructRight a (destructRight b xs)
76 |
77 |
78 |
79 | -- GET FIRST TWO
80 |
81 |
82 | getFirstTwo :: OneOrMore a -> OneOrMore a -> (a,a)
83 | getFirstTwo left right =
84 | case left of
85 | One x ->
86 | (x, getFirstOne right)
87 |
88 | More lleft lright ->
89 | getFirstTwo lleft lright
90 |
91 |
92 | getFirstOne :: OneOrMore a -> a
93 | getFirstOne oneOrMore =
94 | case oneOrMore of
95 | One x ->
96 | x
97 |
98 | More left _ ->
99 | getFirstOne left
100 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Compiler/Imports.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Elm.Compiler.Imports
4 | ( defaults
5 | )
6 | where
7 |
8 |
9 | import qualified Data.Name as Name
10 |
11 | import qualified AST.Source as Src
12 | import qualified Elm.ModuleName as ModuleName
13 | import qualified Reporting.Annotation as A
14 |
15 |
16 |
17 | -- DEFAULTS
18 |
19 |
20 | defaults :: [Src.Import]
21 | defaults =
22 | [ import_ ModuleName.basics Nothing Src.Open
23 | , import_ ModuleName.debug Nothing closed
24 | , import_ ModuleName.list Nothing (operator "::")
25 | , import_ ModuleName.maybe Nothing (typeOpen Name.maybe)
26 | , import_ ModuleName.result Nothing (typeOpen Name.result)
27 | , import_ ModuleName.string Nothing (typeClosed Name.string)
28 | , import_ ModuleName.char Nothing (typeClosed Name.char)
29 | , import_ ModuleName.tuple Nothing closed
30 | , import_ ModuleName.platform Nothing (typeClosed Name.program)
31 | , import_ ModuleName.cmd (Just Name.cmd) (typeClosed Name.cmd)
32 | , import_ ModuleName.sub (Just Name.sub) (typeClosed Name.sub)
33 | ]
34 |
35 |
36 | import_ :: ModuleName.Canonical -> Maybe Name.Name -> Src.Exposing -> Src.Import
37 | import_ (ModuleName.Canonical _ name) maybeAlias exposing =
38 | Src.Import (A.At A.zero name) maybeAlias exposing
39 |
40 |
41 |
42 | -- EXPOSING
43 |
44 |
45 | closed :: Src.Exposing
46 | closed =
47 | Src.Explicit []
48 |
49 |
50 | typeOpen :: Name.Name -> Src.Exposing
51 | typeOpen name =
52 | Src.Explicit [ Src.Upper (A.At A.zero name) (Src.Public A.zero) ]
53 |
54 |
55 | typeClosed :: Name.Name -> Src.Exposing
56 | typeClosed name =
57 | Src.Explicit [ Src.Upper (A.At A.zero name) Src.Private ]
58 |
59 |
60 | operator :: Name.Name -> Src.Exposing
61 | operator op =
62 | Src.Explicit [ Src.Operator A.zero op ]
63 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Float.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE EmptyDataDecls, FlexibleInstances #-}
3 | module Elm.Float
4 | ( Float
5 | , fromPtr
6 | , toBuilder
7 | )
8 | where
9 |
10 |
11 | import Prelude hiding (Float)
12 | import Data.Binary (Binary, get, put)
13 | import qualified Data.ByteString.Builder as B
14 | import qualified Data.Utf8 as Utf8
15 | import Data.Word (Word8)
16 | import Foreign.Ptr (Ptr)
17 |
18 |
19 |
20 | -- FLOATS
21 |
22 |
23 | type Float =
24 | Utf8.Utf8 ELM_FLOAT
25 |
26 |
27 | data ELM_FLOAT
28 |
29 |
30 |
31 | -- HELPERS
32 |
33 |
34 | fromPtr :: Ptr Word8 -> Ptr Word8 -> Float
35 | fromPtr =
36 | Utf8.fromPtr
37 |
38 |
39 | {-# INLINE toBuilder #-}
40 | toBuilder :: Float -> B.Builder
41 | toBuilder =
42 | Utf8.toBuilder
43 |
44 |
45 |
46 | -- BINARY
47 |
48 |
49 | instance Binary (Utf8.Utf8 ELM_FLOAT) where
50 | get = Utf8.getUnder256
51 | put = Utf8.putUnder256
52 |
--------------------------------------------------------------------------------
/compiler/src/Elm/Magnitude.hs:
--------------------------------------------------------------------------------
1 | module Elm.Magnitude
2 | ( Magnitude(..)
3 | , toChars
4 | )
5 | where
6 |
7 |
8 |
9 | -- MAGNITUDE
10 |
11 |
12 | data Magnitude
13 | = PATCH
14 | | MINOR
15 | | MAJOR
16 | deriving (Eq, Ord)
17 |
18 |
19 | toChars :: Magnitude -> String
20 | toChars magnitude =
21 | case magnitude of
22 | PATCH -> "PATCH"
23 | MINOR -> "MINOR"
24 | MAJOR -> "MAJOR"
25 |
--------------------------------------------------------------------------------
/compiler/src/Elm/String.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall -fno-warn-name-shadowing #-}
2 | {-# LANGUAGE BangPatterns, EmptyDataDecls, FlexibleInstances #-}
3 | module Elm.String
4 | ( String
5 | , toChars
6 | , toBuilder
7 | , Chunk(..)
8 | , fromChunks
9 | )
10 | where
11 |
12 |
13 | import Prelude hiding (String)
14 | import Data.Binary (Binary, get, put)
15 | import Data.Bits ((.&.), shiftR)
16 | import qualified Data.ByteString.Builder as B
17 | import qualified Data.Utf8 as Utf8
18 | import Data.Utf8 (MBA, newByteArray, copyFromPtr, freeze, writeWord8)
19 | import GHC.Exts (RealWorld, Ptr)
20 | import GHC.IO (stToIO, unsafeDupablePerformIO)
21 | import GHC.ST (ST)
22 | import GHC.Word (Word8)
23 |
24 |
25 |
26 | -- STRINGS
27 |
28 |
29 | type String =
30 | Utf8.Utf8 ELM_STRING
31 |
32 |
33 | data ELM_STRING
34 |
35 |
36 |
37 | -- HELPERS
38 |
39 |
40 | toChars :: String -> [Char]
41 | toChars =
42 | Utf8.toChars
43 |
44 |
45 | {-# INLINE toBuilder #-}
46 | toBuilder :: String -> B.Builder
47 | toBuilder =
48 | Utf8.toBuilder
49 |
50 |
51 |
52 | -- FROM CHUNKS
53 |
54 |
55 | data Chunk
56 | = Slice (Ptr Word8) Int
57 | | Escape Word8
58 | | CodePoint Int
59 |
60 |
61 | fromChunks :: [Chunk] -> String
62 | fromChunks chunks =
63 | unsafeDupablePerformIO (stToIO (
64 | do let !len = sum (map chunkToWidth chunks)
65 | mba <- newByteArray len
66 | writeChunks mba 0 chunks
67 | freeze mba
68 | ))
69 |
70 |
71 | chunkToWidth :: Chunk -> Int
72 | chunkToWidth chunk =
73 | case chunk of
74 | Slice _ len -> len
75 | Escape _ -> 2
76 | CodePoint c -> if c < 0xFFFF then 6 else 12
77 |
78 |
79 | writeChunks :: MBA RealWorld -> Int -> [Chunk] -> ST RealWorld ()
80 | writeChunks mba offset chunks =
81 | case chunks of
82 | [] ->
83 | return ()
84 |
85 | chunk : chunks ->
86 | case chunk of
87 | Slice ptr len ->
88 | do copyFromPtr ptr mba offset len
89 | let !newOffset = offset + len
90 | writeChunks mba newOffset chunks
91 |
92 | Escape word ->
93 | do writeWord8 mba offset 0x5C {- \ -}
94 | writeWord8 mba (offset + 1) word
95 | let !newOffset = offset + 2
96 | writeChunks mba newOffset chunks
97 |
98 | CodePoint code ->
99 | if code < 0xFFFF then
100 | do writeCode mba offset code
101 | let !newOffset = offset + 6
102 | writeChunks mba newOffset chunks
103 | else
104 | do let (hi,lo) = divMod (code - 0x10000) 0x400
105 | writeCode mba (offset ) (hi + 0xD800)
106 | writeCode mba (offset + 6) (lo + 0xDC00)
107 | let !newOffset = offset + 12
108 | writeChunks mba newOffset chunks
109 |
110 |
111 | writeCode :: MBA RealWorld -> Int -> Int -> ST RealWorld ()
112 | writeCode mba offset code =
113 | do writeWord8 mba offset 0x5C {- \ -}
114 | writeWord8 mba (offset + 1) 0x75 {- u -}
115 | writeHex mba (offset + 2) (shiftR code 12)
116 | writeHex mba (offset + 3) (shiftR code 8)
117 | writeHex mba (offset + 4) (shiftR code 4)
118 | writeHex mba (offset + 5) code
119 |
120 |
121 | writeHex :: MBA RealWorld -> Int -> Int -> ST RealWorld ()
122 | writeHex mba !offset !bits =
123 | do let !n = fromIntegral bits .&. 0x0F
124 | writeWord8 mba offset (if n < 10 then 0x30 + n else 0x37 + n)
125 |
126 |
127 |
128 | -- BINARY
129 |
130 |
131 | instance Binary (Utf8.Utf8 ELM_STRING) where
132 | get = Utf8.getVeryLong
133 | put = Utf8.putVeryLong
134 |
--------------------------------------------------------------------------------
/compiler/src/Generate/Html.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | {-# LANGUAGE QuasiQuotes #-}
3 | module Generate.Html
4 | ( sandwich
5 | )
6 | where
7 |
8 |
9 | import qualified Data.ByteString.Builder as B
10 | import Data.Monoid ((<>))
11 | import qualified Data.Name as Name
12 | import Text.RawString.QQ (r)
13 |
14 |
15 |
16 | -- SANDWICH
17 |
18 |
19 | sandwich :: Name.Name -> B.Builder -> B.Builder
20 | sandwich moduleName javascript =
21 | let name = Name.toBuilder moduleName in
22 | [r|
23 |
24 |
25 |
26 | |] <> name <> [r|
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
52 |
53 |
54 | |]
55 |
--------------------------------------------------------------------------------
/compiler/src/Generate/JavaScript/Functions.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings, QuasiQuotes #-}
2 | module Generate.JavaScript.Functions
3 | ( functions
4 | )
5 | where
6 |
7 |
8 | import qualified Data.ByteString.Builder as B
9 | import Text.RawString.QQ (r)
10 |
11 |
12 |
13 | -- FUNCTIONS
14 |
15 |
16 | functions :: B.Builder
17 | functions = [r|
18 |
19 | function F(arity, fun, wrapper) {
20 | wrapper.a = arity;
21 | wrapper.f = fun;
22 | return wrapper;
23 | }
24 |
25 | function F2(fun) {
26 | return F(2, fun, function(a) { return function(b) { return fun(a,b); }; })
27 | }
28 | function F3(fun) {
29 | return F(3, fun, function(a) {
30 | return function(b) { return function(c) { return fun(a, b, c); }; };
31 | });
32 | }
33 | function F4(fun) {
34 | return F(4, fun, function(a) { return function(b) { return function(c) {
35 | return function(d) { return fun(a, b, c, d); }; }; };
36 | });
37 | }
38 | function F5(fun) {
39 | return F(5, fun, function(a) { return function(b) { return function(c) {
40 | return function(d) { return function(e) { return fun(a, b, c, d, e); }; }; }; };
41 | });
42 | }
43 | function F6(fun) {
44 | return F(6, fun, function(a) { return function(b) { return function(c) {
45 | return function(d) { return function(e) { return function(f) {
46 | return fun(a, b, c, d, e, f); }; }; }; }; };
47 | });
48 | }
49 | function F7(fun) {
50 | return F(7, fun, function(a) { return function(b) { return function(c) {
51 | return function(d) { return function(e) { return function(f) {
52 | return function(g) { return fun(a, b, c, d, e, f, g); }; }; }; }; }; };
53 | });
54 | }
55 | function F8(fun) {
56 | return F(8, fun, function(a) { return function(b) { return function(c) {
57 | return function(d) { return function(e) { return function(f) {
58 | return function(g) { return function(h) {
59 | return fun(a, b, c, d, e, f, g, h); }; }; }; }; }; }; };
60 | });
61 | }
62 | function F9(fun) {
63 | return F(9, fun, function(a) { return function(b) { return function(c) {
64 | return function(d) { return function(e) { return function(f) {
65 | return function(g) { return function(h) { return function(i) {
66 | return fun(a, b, c, d, e, f, g, h, i); }; }; }; }; }; }; }; };
67 | });
68 | }
69 |
70 | function A2(fun, a, b) {
71 | return fun.a === 2 ? fun.f(a, b) : fun(a)(b);
72 | }
73 | function A3(fun, a, b, c) {
74 | return fun.a === 3 ? fun.f(a, b, c) : fun(a)(b)(c);
75 | }
76 | function A4(fun, a, b, c, d) {
77 | return fun.a === 4 ? fun.f(a, b, c, d) : fun(a)(b)(c)(d);
78 | }
79 | function A5(fun, a, b, c, d, e) {
80 | return fun.a === 5 ? fun.f(a, b, c, d, e) : fun(a)(b)(c)(d)(e);
81 | }
82 | function A6(fun, a, b, c, d, e, f) {
83 | return fun.a === 6 ? fun.f(a, b, c, d, e, f) : fun(a)(b)(c)(d)(e)(f);
84 | }
85 | function A7(fun, a, b, c, d, e, f, g) {
86 | return fun.a === 7 ? fun.f(a, b, c, d, e, f, g) : fun(a)(b)(c)(d)(e)(f)(g);
87 | }
88 | function A8(fun, a, b, c, d, e, f, g, h) {
89 | return fun.a === 8 ? fun.f(a, b, c, d, e, f, g, h) : fun(a)(b)(c)(d)(e)(f)(g)(h);
90 | }
91 | function A9(fun, a, b, c, d, e, f, g, h, i) {
92 | return fun.a === 9 ? fun.f(a, b, c, d, e, f, g, h, i) : fun(a)(b)(c)(d)(e)(f)(g)(h)(i);
93 | }
94 |
95 | |]
96 |
--------------------------------------------------------------------------------
/compiler/src/Generate/Mode.hs:
--------------------------------------------------------------------------------
1 | module Generate.Mode
2 | ( Mode(..)
3 | , isDebug
4 | , ShortFieldNames
5 | , shortenFieldNames
6 | )
7 | where
8 |
9 |
10 | import qualified Data.List as List
11 | import qualified Data.Map as Map
12 | import qualified Data.Maybe as Maybe
13 | import qualified Data.Name as Name
14 |
15 | import qualified AST.Optimized as Opt
16 | import qualified Elm.Compiler.Type.Extract as Extract
17 | import qualified Generate.JavaScript.Name as JsName
18 |
19 |
20 |
21 | -- MODE
22 |
23 |
24 | data Mode
25 | = Dev (Maybe Extract.Types)
26 | | Prod ShortFieldNames
27 |
28 |
29 | isDebug :: Mode -> Bool
30 | isDebug mode =
31 | case mode of
32 | Dev mi -> Maybe.isJust mi
33 | Prod _ -> False
34 |
35 |
36 |
37 | -- SHORTEN FIELD NAMES
38 |
39 |
40 | type ShortFieldNames =
41 | Map.Map Name.Name JsName.Name
42 |
43 |
44 | shortenFieldNames :: Opt.GlobalGraph -> ShortFieldNames
45 | shortenFieldNames (Opt.GlobalGraph _ frequencies) =
46 | Map.foldr addToShortNames Map.empty $
47 | Map.foldrWithKey addToBuckets Map.empty frequencies
48 |
49 |
50 | addToBuckets :: Name.Name -> Int -> Map.Map Int [Name.Name] -> Map.Map Int [Name.Name]
51 | addToBuckets field frequency buckets =
52 | Map.insertWith (++) frequency [field] buckets
53 |
54 |
55 | addToShortNames :: [Name.Name] -> ShortFieldNames -> ShortFieldNames
56 | addToShortNames fields shortNames =
57 | List.foldl' addField shortNames fields
58 |
59 |
60 | addField :: ShortFieldNames -> Name.Name -> ShortFieldNames
61 | addField shortNames field =
62 | let rename = JsName.fromInt (Map.size shortNames) in
63 | Map.insert field rename shortNames
64 |
--------------------------------------------------------------------------------
/compiler/src/Nitpick/Debug.hs:
--------------------------------------------------------------------------------
1 | module Nitpick.Debug
2 | ( hasDebugUses
3 | )
4 | where
5 |
6 |
7 | import qualified Data.Map.Utils as Map
8 |
9 | import qualified AST.Optimized as Opt
10 |
11 |
12 |
13 | -- HAS DEBUG USES
14 |
15 |
16 | hasDebugUses :: Opt.LocalGraph -> Bool
17 | hasDebugUses (Opt.LocalGraph _ graph _) =
18 | Map.any nodeHasDebug graph
19 |
20 |
21 | nodeHasDebug :: Opt.Node -> Bool
22 | nodeHasDebug node =
23 | case node of
24 | Opt.Define expr _ -> hasDebug expr
25 | Opt.DefineTailFunc _ expr _ -> hasDebug expr
26 | Opt.Ctor _ _ -> False
27 | Opt.Enum _ -> False
28 | Opt.Box -> False
29 | Opt.Link _ -> False
30 | Opt.Cycle _ vs fs _ -> any (hasDebug . snd) vs || any defHasDebug fs
31 | Opt.Manager _ -> False
32 | Opt.Kernel _ _ -> False
33 | Opt.PortIncoming expr _ -> hasDebug expr
34 | Opt.PortOutgoing expr _ -> hasDebug expr
35 |
36 |
37 | hasDebug :: Opt.Expr -> Bool
38 | hasDebug expression =
39 | case expression of
40 | Opt.Bool _ -> False
41 | Opt.Chr _ -> False
42 | Opt.Str _ -> False
43 | Opt.Int _ -> False
44 | Opt.Float _ -> False
45 | Opt.VarLocal _ -> False
46 | Opt.VarGlobal _ -> False
47 | Opt.VarEnum _ _ -> False
48 | Opt.VarBox _ -> False
49 | Opt.VarCycle _ _ -> False
50 | Opt.VarDebug _ _ _ _ -> True
51 | Opt.VarKernel _ _ -> False
52 | Opt.List exprs -> any hasDebug exprs
53 | Opt.Function _ expr -> hasDebug expr
54 | Opt.Call e es -> hasDebug e || any hasDebug es
55 | Opt.TailCall _ args -> any (hasDebug . snd) args
56 | Opt.If conds finally -> any (\(c,e) -> hasDebug c || hasDebug e) conds || hasDebug finally
57 | Opt.Let def body -> defHasDebug def || hasDebug body
58 | Opt.Destruct _ expr -> hasDebug expr
59 | Opt.Case _ _ d jumps -> deciderHasDebug d || any (hasDebug . snd) jumps
60 | Opt.Accessor _ -> False
61 | Opt.Access r _ -> hasDebug r
62 | Opt.Update r fs -> hasDebug r || any hasDebug fs
63 | Opt.Record fs -> any hasDebug fs
64 | Opt.Unit -> False
65 | Opt.Tuple a b c -> hasDebug a || hasDebug b || maybe False hasDebug c
66 | Opt.Shader _ _ _ -> False
67 |
68 |
69 | defHasDebug :: Opt.Def -> Bool
70 | defHasDebug def =
71 | case def of
72 | Opt.Def _ expr -> hasDebug expr
73 | Opt.TailDef _ _ expr -> hasDebug expr
74 |
75 |
76 | deciderHasDebug :: Opt.Decider Opt.Choice -> Bool
77 | deciderHasDebug decider =
78 | case decider of
79 | Opt.Leaf (Opt.Inline expr) -> hasDebug expr
80 | Opt.Leaf (Opt.Jump _) -> False
81 | Opt.Chain _ success failure -> deciderHasDebug success || deciderHasDebug failure
82 | Opt.FanOut _ tests fallback -> any (deciderHasDebug . snd) tests || deciderHasDebug fallback
83 |
84 |
85 |
86 | -- TODO: FIND GLOBALLY UNUSED DEFINITIONS?
87 | -- TODO: FIND PACKAGE USAGE STATS? (e.g. elm/core = 142, author/project = 2, etc.)
88 |
--------------------------------------------------------------------------------
/compiler/src/Parse/Symbol.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE BangPatterns, OverloadedStrings #-}
3 | module Parse.Symbol
4 | ( operator
5 | , BadOperator(..)
6 | , binopCharSet
7 | )
8 | where
9 |
10 |
11 | import qualified Data.Char as Char
12 | import qualified Data.IntSet as IntSet
13 | import qualified Data.Name as Name
14 | import qualified Data.Vector as Vector
15 | import Foreign.Ptr (Ptr, plusPtr, minusPtr)
16 | import GHC.Word (Word8)
17 |
18 | import Parse.Primitives (Parser, Row, Col)
19 | import qualified Parse.Primitives as P
20 |
21 |
22 |
23 | -- OPERATOR
24 |
25 |
26 | data BadOperator
27 | = BadDot
28 | | BadPipe
29 | | BadArrow
30 | | BadEquals
31 | | BadHasType
32 |
33 |
34 | operator :: (Row -> Col -> x) -> (BadOperator -> Row -> Col -> x) -> Parser x Name.Name
35 | operator toExpectation toError =
36 | P.Parser $ \(P.State src pos end indent row col) cok _ cerr eerr ->
37 | let !newPos = chompOps pos end in
38 | if pos == newPos then
39 | eerr row col toExpectation
40 |
41 | else
42 | case Name.fromPtr pos newPos of
43 | "." -> eerr row col (toError BadDot)
44 | "|" -> cerr row col (toError BadPipe)
45 | "->" -> cerr row col (toError BadArrow)
46 | "=" -> cerr row col (toError BadEquals)
47 | ":" -> cerr row col (toError BadHasType)
48 | op ->
49 | let
50 | !newCol = col + fromIntegral (minusPtr newPos pos)
51 | !newState = P.State src newPos end indent row newCol
52 | in
53 | cok op newState
54 |
55 |
56 | chompOps :: Ptr Word8 -> Ptr Word8 -> Ptr Word8
57 | chompOps pos end =
58 | if pos < end && isBinopCharHelp (P.unsafeIndex pos) then
59 | chompOps (plusPtr pos 1) end
60 | else
61 | pos
62 |
63 |
64 | {-# INLINE isBinopCharHelp #-}
65 | isBinopCharHelp :: Word8 -> Bool
66 | isBinopCharHelp word =
67 | word < 128 && Vector.unsafeIndex binopCharVector (fromIntegral word)
68 |
69 |
70 | {-# NOINLINE binopCharVector #-}
71 | binopCharVector :: Vector.Vector Bool
72 | binopCharVector =
73 | Vector.generate 128 (\i -> IntSet.member i binopCharSet)
74 |
75 |
76 | {-# NOINLINE binopCharSet #-}
77 | binopCharSet :: IntSet.IntSet
78 | binopCharSet =
79 | IntSet.fromList (map Char.ord "+-/*=.<>:&|^?%!")
80 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Annotation.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | module Reporting.Annotation
3 | ( Located(..)
4 | , Position(..)
5 | , Region(..)
6 | , traverse
7 | , toValue
8 | , merge
9 | , at
10 | , toRegion
11 | , mergeRegions
12 | , zero
13 | , one
14 | )
15 | where
16 |
17 |
18 | import Prelude hiding (traverse)
19 | import Control.Monad (liftM2)
20 | import Data.Binary (Binary, get, put)
21 | import Data.Word (Word16)
22 |
23 |
24 |
25 | -- LOCATED
26 |
27 |
28 | data Located a =
29 | At Region a -- PERF see if unpacking region is helpful
30 |
31 |
32 | instance Functor Located where
33 | fmap f (At region a) =
34 | At region (f a)
35 |
36 |
37 | traverse :: (Functor f) => (a -> f b) -> Located a -> f (Located b)
38 | traverse func (At region value) =
39 | At region <$> func value
40 |
41 |
42 | toValue :: Located a -> a
43 | toValue (At _ value) =
44 | value
45 |
46 |
47 | merge :: Located a -> Located b -> value -> Located value
48 | merge (At r1 _) (At r2 _) value =
49 | At (mergeRegions r1 r2) value
50 |
51 |
52 |
53 | -- POSITION
54 |
55 |
56 | data Position =
57 | Position
58 | {-# UNPACK #-} !Word16
59 | {-# UNPACK #-} !Word16
60 | deriving (Eq)
61 |
62 |
63 | at :: Position -> Position -> a -> Located a
64 | at start end a =
65 | At (Region start end) a
66 |
67 |
68 |
69 | -- REGION
70 |
71 |
72 | data Region = Region Position Position
73 | deriving (Eq)
74 |
75 |
76 | toRegion :: Located a -> Region
77 | toRegion (At region _) =
78 | region
79 |
80 |
81 | mergeRegions :: Region -> Region -> Region
82 | mergeRegions (Region start _) (Region _ end) =
83 | Region start end
84 |
85 |
86 | zero :: Region
87 | zero =
88 | Region (Position 0 0) (Position 0 0)
89 |
90 |
91 | one :: Region
92 | one =
93 | Region (Position 1 1) (Position 1 1)
94 |
95 |
96 | instance Binary Region where
97 | put (Region a b) = put a >> put b
98 | get = liftM2 Region get get
99 |
100 |
101 | instance Binary Position where
102 | put (Position a b) = put a >> put b
103 | get = liftM2 Position get get
104 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Error/Main.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Error.Main
4 | ( Error(..)
5 | , toReport
6 | )
7 | where
8 |
9 |
10 | import qualified Data.Name as Name
11 |
12 | import qualified AST.Canonical as Can
13 | import qualified Reporting.Annotation as A
14 | import qualified Reporting.Doc as D
15 | import qualified Reporting.Error.Canonicalize as E
16 | import qualified Reporting.Render.Code as Code
17 | import qualified Reporting.Render.Type as RT
18 | import qualified Reporting.Render.Type.Localizer as L
19 | import qualified Reporting.Report as Report
20 |
21 |
22 |
23 | -- ERROR
24 |
25 |
26 | data Error
27 | = BadType A.Region Can.Type
28 | | BadCycle A.Region Name.Name [Name.Name]
29 | | BadFlags A.Region Can.Type E.InvalidPayload
30 |
31 |
32 |
33 | -- TO REPORT
34 |
35 |
36 | toReport :: L.Localizer -> Code.Source -> Error -> Report.Report
37 | toReport localizer source err =
38 | case err of
39 | BadType region tipe ->
40 | Report.Report "BAD MAIN TYPE" region [] $
41 | Code.toSnippet source region Nothing
42 | (
43 | "I cannot handle this type of `main` value:"
44 | ,
45 | D.stack
46 | [ "The type of `main` value I am seeing is:"
47 | , D.indent 4 $ D.dullyellow $ RT.canToDoc localizer RT.None tipe
48 | , D.reflow $
49 | "I only know how to handle Html, Svg, and Programs\
50 | \ though. Modify `main` to be one of those types of values!"
51 | ]
52 | )
53 |
54 | BadCycle region name names ->
55 | Report.Report "BAD MAIN" region [] $
56 | Code.toSnippet source region Nothing
57 | (
58 | "A `main` definition cannot be defined in terms of itself."
59 | ,
60 | D.stack
61 | [ D.reflow $
62 | "It should be a boring value with no recursion. But\
63 | \ instead it is involved in this cycle of definitions:"
64 | , D.cycle 4 name names
65 | ]
66 | )
67 |
68 | BadFlags region _badType invalidPayload ->
69 | let
70 | formatDetails (aBadKindOfThing, butThatIsNoGood) =
71 | Report.Report "BAD FLAGS" region [] $
72 | Code.toSnippet source region Nothing
73 | (
74 | D.reflow $
75 | "Your `main` program wants " ++ aBadKindOfThing ++ " from JavaScript."
76 | ,
77 | butThatIsNoGood
78 | )
79 | in
80 | formatDetails $
81 | case invalidPayload of
82 | E.ExtendedRecord ->
83 | (
84 | "an extended record"
85 | ,
86 | D.reflow $
87 | "But the exact shape of the record must be known at compile time. No type variables!"
88 | )
89 |
90 | E.Function ->
91 | (
92 | "a function"
93 | ,
94 | D.reflow $
95 | "But if I allowed functions from JS, it would be possible to sneak\
96 | \ side-effects and runtime exceptions into Elm!"
97 | )
98 |
99 | E.TypeVariable name ->
100 | (
101 | "an unspecified type"
102 | ,
103 | D.reflow $
104 | "But type variables like `" ++ Name.toChars name ++ "` cannot be given as flags.\
105 | \ I need to know exactly what type of data I am getting, so I can guarantee that\
106 | \ unexpected data cannot sneak in and crash the Elm program."
107 | )
108 |
109 | E.UnsupportedType name ->
110 | (
111 | "a `" ++ Name.toChars name ++ "` value"
112 | ,
113 | D.stack
114 | [ D.reflow $ "I cannot handle that. The types that CAN be in flags include:"
115 | , D.indent 4 $
116 | D.reflow $
117 | "Ints, Floats, Bools, Strings, Maybes, Lists, Arrays,\
118 | \ tuples, records, and JSON values."
119 | , D.reflow $
120 | "Since JSON values can flow through, you can use JSON encoders and decoders\
121 | \ to allow other types through as well. More advanced users often just do\
122 | \ everything with encoders and decoders for more control and better errors."
123 | ]
124 | )
125 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Render/Type/Localizer.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Render.Type.Localizer
4 | ( Localizer
5 | , toDoc
6 | , toChars
7 | , empty
8 | , fromNames
9 | , fromModule
10 | )
11 | where
12 |
13 |
14 | import qualified Data.Map as Map
15 | import qualified Data.Name as Name
16 | import qualified Data.Set as Set
17 |
18 | import qualified AST.Source as Src
19 | import qualified Elm.ModuleName as ModuleName
20 | import Reporting.Doc ((<>))
21 | import qualified Reporting.Doc as D
22 | import qualified Reporting.Annotation as A
23 |
24 |
25 |
26 | -- LOCALIZER
27 |
28 |
29 | newtype Localizer =
30 | Localizer (Map.Map Name.Name Import)
31 |
32 |
33 | data Import =
34 | Import
35 | { _alias :: Maybe Name.Name
36 | , _exposing :: Exposing
37 | }
38 |
39 |
40 | data Exposing
41 | = All
42 | | Only (Set.Set Name.Name)
43 |
44 |
45 | empty :: Localizer
46 | empty =
47 | Localizer Map.empty
48 |
49 |
50 |
51 | -- LOCALIZE
52 |
53 |
54 | toDoc :: Localizer -> ModuleName.Canonical -> Name.Name -> D.Doc
55 | toDoc localizer home name =
56 | D.fromChars (toChars localizer home name)
57 |
58 |
59 | toChars :: Localizer -> ModuleName.Canonical -> Name.Name -> String
60 | toChars (Localizer localizer) moduleName@(ModuleName.Canonical _ home) name =
61 | case Map.lookup home localizer of
62 | Nothing ->
63 | Name.toChars home <> "." <> Name.toChars name
64 |
65 | Just (Import alias exposing) ->
66 | case exposing of
67 | All ->
68 | Name.toChars name
69 |
70 | Only set ->
71 | if Set.member name set then
72 | Name.toChars name
73 | else if name == Name.list && moduleName == ModuleName.list then
74 | "List"
75 | else
76 | Name.toChars (maybe home id alias) <> "." <> Name.toChars name
77 |
78 |
79 |
80 | -- FROM NAMES
81 |
82 |
83 | fromNames :: Map.Map Name.Name a -> Localizer
84 | fromNames names =
85 | Localizer $ Map.map (\_ -> Import Nothing All) names
86 |
87 |
88 |
89 | -- FROM MODULE
90 |
91 |
92 | fromModule :: Src.Module -> Localizer
93 | fromModule modul@(Src.Module _ _ _ imports _ _ _ _ _) =
94 | Localizer $ Map.fromList $
95 | (Src.getName modul, Import Nothing All) : map toPair imports
96 |
97 |
98 | toPair :: Src.Import -> (Name.Name, Import)
99 | toPair (Src.Import (A.At _ name) alias exposing) =
100 | ( name
101 | , Import alias (toExposing exposing)
102 | )
103 |
104 |
105 | toExposing :: Src.Exposing -> Exposing
106 | toExposing exposing =
107 | case exposing of
108 | Src.Open ->
109 | All
110 |
111 | Src.Explicit exposedList ->
112 | Only (foldr addType Set.empty exposedList)
113 |
114 |
115 | addType :: Src.Exposed -> Set.Set Name.Name -> Set.Set Name.Name
116 | addType exposed types =
117 | case exposed of
118 | Src.Lower _ -> types
119 | Src.Upper (A.At _ name) _ -> Set.insert name types
120 | Src.Operator _ _ -> types
121 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Report.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Reporting.Report
3 | ( Report(..)
4 | )
5 | where
6 |
7 |
8 | import qualified Reporting.Annotation as A
9 | import qualified Reporting.Doc as D
10 |
11 |
12 |
13 | -- BUILD REPORTS
14 |
15 |
16 | data Report =
17 | Report
18 | { _title :: String
19 | , _region :: A.Region
20 | , _sgstns :: [String]
21 | , _message :: D.Doc
22 | }
23 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Result.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE Rank2Types #-}
3 | module Reporting.Result
4 | ( Result(..)
5 | , run
6 | , ok
7 | , warn
8 | , throw
9 | , mapError
10 | )
11 | where
12 |
13 |
14 | import qualified Data.OneOrMore as OneOrMore
15 | import qualified Reporting.Warning as Warning
16 |
17 |
18 |
19 | -- RESULT
20 |
21 |
22 | newtype Result info warnings error a =
23 | Result (
24 | forall result.
25 | info
26 | -> warnings
27 | -> (info -> warnings -> OneOrMore.OneOrMore error -> result)
28 | -> (info -> warnings -> a -> result)
29 | -> result
30 | )
31 |
32 |
33 | run :: Result () [w] e a -> ([w], Either (OneOrMore.OneOrMore e) a)
34 | run (Result k) =
35 | k () []
36 | (\() w e -> (reverse w, Left e))
37 | (\() w a -> (reverse w, Right a))
38 |
39 |
40 |
41 | -- HELPERS
42 |
43 |
44 | ok :: a -> Result i w e a
45 | ok a =
46 | Result $ \i w _ good ->
47 | good i w a
48 |
49 |
50 | warn :: Warning.Warning -> Result i [Warning.Warning] e ()
51 | warn warning =
52 | Result $ \i warnings _ good ->
53 | good i (warning:warnings) ()
54 |
55 |
56 | throw :: e -> Result i w e a
57 | throw e =
58 | Result $ \i w bad _ ->
59 | bad i w (OneOrMore.one e)
60 |
61 |
62 | mapError :: (e -> e') -> Result i w e a -> Result i w e' a
63 | mapError func (Result k) =
64 | Result $ \i w bad good ->
65 | let
66 | bad1 i1 w1 e1 =
67 | bad i1 w1 (OneOrMore.map func e1)
68 | in
69 | k i w bad1 good
70 |
71 |
72 |
73 | -- FANCY INSTANCE STUFF
74 |
75 |
76 | instance Functor (Result i w e) where
77 | fmap func (Result k) =
78 | Result $ \i w bad good ->
79 | let
80 | good1 i1 w1 value =
81 | good i1 w1 (func value)
82 | in
83 | k i w bad good1
84 |
85 |
86 | instance Applicative (Result i w e) where
87 | pure = ok
88 |
89 | (<*>) (Result kf) (Result kv) =
90 | Result $ \i w bad good ->
91 | let
92 | bad1 i1 w1 e1 =
93 | let
94 | bad2 i2 w2 e2 = bad i2 w2 (OneOrMore.more e1 e2)
95 | good2 i2 w2 _value = bad i2 w2 e1
96 | in
97 | kv i1 w1 bad2 good2
98 |
99 | good1 i1 w1 func =
100 | let
101 | bad2 i2 w2 e2 = bad i2 w2 e2
102 | good2 i2 w2 value = good i2 w2 (func value)
103 | in
104 | kv i1 w1 bad2 good2
105 | in
106 | kf i w bad1 good1
107 |
108 |
109 | instance Monad (Result i w e) where
110 | return = ok
111 |
112 | (>>=) (Result ka) callback =
113 | Result $ \i w bad good ->
114 | let
115 | good1 i1 w1 a =
116 | case callback a of
117 | Result kb -> kb i1 w1 bad good
118 | in
119 | ka i w bad good1
120 |
121 | (>>) (Result ka) (Result kb) =
122 | Result $ \i w bad good ->
123 | let
124 | good1 i1 w1 _ =
125 | kb i1 w1 bad good
126 | in
127 | ka i w bad good1
128 |
129 | -- PERF add INLINE to these?
130 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Suggest.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Suggest
4 | ( distance
5 | , sort
6 | , rank
7 | )
8 | where
9 |
10 |
11 | import qualified Data.Char as Char
12 | import qualified Data.List as List
13 | import qualified Text.EditDistance as Dist
14 |
15 |
16 |
17 | -- DISTANCE
18 |
19 |
20 | distance :: String -> String -> Int
21 | distance x y =
22 | Dist.restrictedDamerauLevenshteinDistance Dist.defaultEditCosts x y
23 |
24 |
25 |
26 | -- SORT
27 |
28 |
29 | sort :: String -> (a -> String) -> [a] -> [a]
30 | sort target toString values =
31 | List.sortOn (distance (toLower target) . toLower . toString) values
32 |
33 |
34 | toLower :: String -> String
35 | toLower string =
36 | map Char.toLower string
37 |
38 |
39 |
40 | -- RANK
41 |
42 |
43 | rank :: String -> (a -> String) -> [a] -> [(Int,a)]
44 | rank target toString values =
45 | let
46 | toRank v =
47 | distance (toLower target) (toLower (toString v))
48 |
49 | addRank v =
50 | (toRank v, v)
51 | in
52 | List.sortOn fst (map addRank values)
53 |
--------------------------------------------------------------------------------
/compiler/src/Reporting/Warning.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Reporting.Warning
4 | ( Warning(..)
5 | , Context(..)
6 | , toReport
7 | )
8 | where
9 |
10 |
11 | import Data.Monoid ((<>))
12 | import qualified Data.Name as Name
13 |
14 | import qualified AST.Canonical as Can
15 | import qualified AST.Utils.Type as Type
16 | import qualified Reporting.Annotation as A
17 | import qualified Reporting.Doc as D
18 | import qualified Reporting.Report as Report
19 | import qualified Reporting.Render.Code as Code
20 | import qualified Reporting.Render.Type as RT
21 | import qualified Reporting.Render.Type.Localizer as L
22 |
23 |
24 |
25 | -- ALL POSSIBLE WARNINGS
26 |
27 |
28 | data Warning
29 | = UnusedImport A.Region Name.Name
30 | | UnusedVariable A.Region Context Name.Name
31 | | MissingTypeAnnotation A.Region Name.Name Can.Type
32 |
33 |
34 | data Context = Def | Pattern
35 |
36 |
37 |
38 | -- TO REPORT
39 |
40 |
41 | toReport :: L.Localizer -> Code.Source -> Warning -> Report.Report
42 | toReport localizer source warning =
43 | case warning of
44 | UnusedImport region moduleName ->
45 | Report.Report "unused import" region [] $
46 | Code.toSnippet source region Nothing
47 | (
48 | D.reflow $
49 | "Nothing from the `" <> Name.toChars moduleName <> "` module is used in this file."
50 | ,
51 | "I recommend removing unused imports."
52 | )
53 |
54 | UnusedVariable region context name ->
55 | let title = defOrPat context "unused definition" "unused variable" in
56 | Report.Report title region [] $
57 | Code.toSnippet source region Nothing
58 | (
59 | D.reflow $
60 | "You are not using `" <> Name.toChars name <> "` anywhere."
61 | ,
62 | D.stack
63 | [ D.reflow $
64 | "Is there a typo? Maybe you intended to use `" <> Name.toChars name
65 | <> "` somewhere but typed another name instead?"
66 | , D.reflow $
67 | defOrPat context
68 | ( "If you are sure there is no typo, remove the definition.\
69 | \ This way future readers will not have to wonder why it is there!"
70 | )
71 | ( "If you are sure there is no typo, replace `" <> Name.toChars name
72 | <> "` with _ so future readers will not have to wonder why it is there!"
73 | )
74 | ]
75 | )
76 |
77 | MissingTypeAnnotation region name inferredType ->
78 | Report.Report "missing type annotation" region [] $
79 | Code.toSnippet source region Nothing
80 | (
81 | D.reflow $
82 | case Type.deepDealias inferredType of
83 | Can.TLambda _ _ ->
84 | "The `" <> Name.toChars name <> "` function has no type annotation."
85 |
86 | _ ->
87 | "The `" <> Name.toChars name <> "` definition has no type annotation."
88 | ,
89 | D.stack
90 | [ "I inferred the type annotation myself though! You can copy it into your code:"
91 | , D.green $ D.hang 4 $ D.sep $
92 | [ D.fromName name <> " :"
93 | , RT.canToDoc localizer RT.None inferredType
94 | ]
95 | ]
96 | )
97 |
98 |
99 | defOrPat :: Context -> a -> a -> a
100 | defOrPat context def pat =
101 | case context of
102 | Def -> def
103 | Pattern -> pat
104 |
105 |
--------------------------------------------------------------------------------
/compiler/src/Type/Instantiate.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Type.Instantiate
4 | ( FreeVars
5 | , fromSrcType
6 | )
7 | where
8 |
9 |
10 | import qualified Data.Map.Strict as Map
11 | import Data.Map.Strict ((!))
12 | import qualified Data.Name as Name
13 |
14 | import qualified AST.Canonical as Can
15 | import Type.Type
16 |
17 |
18 |
19 | -- FREE VARS
20 |
21 |
22 | type FreeVars =
23 | Map.Map Name.Name Type
24 |
25 |
26 |
27 | -- FROM SOURCE TYPE
28 |
29 |
30 | fromSrcType :: Map.Map Name.Name Type -> Can.Type -> IO Type
31 | fromSrcType freeVars sourceType =
32 | case sourceType of
33 | Can.TLambda arg result ->
34 | FunN
35 | <$> fromSrcType freeVars arg
36 | <*> fromSrcType freeVars result
37 |
38 | Can.TVar name ->
39 | return (freeVars ! name)
40 |
41 | Can.TType home name args ->
42 | AppN home name <$> traverse (fromSrcType freeVars) args
43 |
44 | Can.TAlias home name args aliasedType ->
45 | do targs <- traverse (traverse (fromSrcType freeVars)) args
46 | AliasN home name targs <$>
47 | case aliasedType of
48 | Can.Filled realType ->
49 | fromSrcType freeVars realType
50 |
51 | Can.Holey realType ->
52 | fromSrcType (Map.fromList targs) realType
53 |
54 | Can.TTuple a b maybeC ->
55 | TupleN
56 | <$> fromSrcType freeVars a
57 | <*> fromSrcType freeVars b
58 | <*> traverse (fromSrcType freeVars) maybeC
59 |
60 | Can.TUnit ->
61 | return UnitN
62 |
63 | Can.TRecord fields maybeExt ->
64 | RecordN
65 | <$> traverse (fromSrcFieldType freeVars) fields
66 | <*>
67 | case maybeExt of
68 | Nothing ->
69 | return EmptyRecordN
70 |
71 | Just ext ->
72 | return (freeVars ! ext)
73 |
74 |
75 | fromSrcFieldType :: Map.Map Name.Name Type -> Can.FieldType -> IO Type
76 | fromSrcFieldType freeVars (Can.FieldType _ tipe) =
77 | fromSrcType freeVars tipe
78 |
--------------------------------------------------------------------------------
/compiler/src/Type/Occurs.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | module Type.Occurs
4 | ( occurs
5 | )
6 | where
7 |
8 |
9 | import Data.Foldable (foldrM)
10 | import qualified Data.Map.Strict as Map
11 |
12 | import Type.Type as Type
13 | import qualified Type.UnionFind as UF
14 |
15 |
16 |
17 | -- OCCURS
18 |
19 |
20 | occurs :: Type.Variable -> IO Bool
21 | occurs var =
22 | occursHelp [] var False
23 |
24 |
25 | occursHelp :: [Type.Variable] -> Type.Variable -> Bool -> IO Bool
26 | occursHelp seen var foundCycle =
27 | if elem var seen then
28 | return True
29 |
30 | else
31 | do (Descriptor content _ _ _) <- UF.get var
32 | case content of
33 | FlexVar _ ->
34 | return foundCycle
35 |
36 | FlexSuper _ _ ->
37 | return foundCycle
38 |
39 | RigidVar _ ->
40 | return foundCycle
41 |
42 | RigidSuper _ _ ->
43 | return foundCycle
44 |
45 | Structure term ->
46 | let newSeen = var : seen in
47 | case term of
48 | App1 _ _ args ->
49 | foldrM (occursHelp newSeen) foundCycle args
50 |
51 | Fun1 a b ->
52 | occursHelp newSeen a =<<
53 | occursHelp newSeen b foundCycle
54 |
55 | EmptyRecord1 ->
56 | return foundCycle
57 |
58 | Record1 fields ext ->
59 | occursHelp newSeen ext =<<
60 | foldrM (occursHelp newSeen) foundCycle (Map.elems fields)
61 |
62 | Unit1 ->
63 | return foundCycle
64 |
65 | Tuple1 a b maybeC ->
66 | case maybeC of
67 | Nothing ->
68 | occursHelp newSeen a =<<
69 | occursHelp newSeen b foundCycle
70 |
71 | Just c ->
72 | occursHelp newSeen a =<<
73 | occursHelp newSeen b =<<
74 | occursHelp newSeen c foundCycle
75 |
76 | Alias _ _ args _ ->
77 | foldrM (occursHelp (var:seen)) foundCycle (map snd args)
78 |
79 | Error ->
80 | return foundCycle
81 |
--------------------------------------------------------------------------------
/docs/elm.json/application.md:
--------------------------------------------------------------------------------
1 | # `elm.json` for applications
2 |
3 | This is a decent baseline for pretty much any applications made with Elm. You will need these dependencies or more.
4 |
5 | ```json
6 | {
7 | "type": "application",
8 | "source-directories": [
9 | "src"
10 | ],
11 | "elm-version": "0.19.1",
12 | "dependencies": {
13 | "direct": {
14 | "elm/browser": "1.0.0",
15 | "elm/core": "1.0.0",
16 | "elm/html": "1.0.0",
17 | "elm/json": "1.0.0"
18 | },
19 | "indirect": {
20 | "elm/time": "1.0.0",
21 | "elm/url": "1.0.0",
22 | "elm/virtual-dom": "1.0.0"
23 | }
24 | },
25 | "test-dependencies": {
26 | "direct": {},
27 | "indirect": {}
28 | }
29 | }
30 | ```
31 |
32 |
33 |
34 |
35 | ## `"type"`
36 |
37 | Either `"application"` or `"package"`. All the other fields are based on this choice!
38 |
39 |
40 |
41 |
42 | ## `"source-directories"`
43 |
44 | A list of directories where Elm code lives. Most projects just use `"src"` for everything.
45 |
46 |
47 |
48 |
49 | ## `"elm-version"`
50 |
51 | The exact version of Elm this builds with. Should be `"0.19.1"` for most people!
52 |
53 |
54 |
55 |
56 | ## `"dependencies"`
57 |
58 | All the packages you depend upon. We use exact versions, so your `elm.json` file doubles as a "lock file" that ensures reliable builds.
59 |
60 | You can use modules from any `"direct"` dependency in your code. Some `"direct"` dependencies have their own dependencies that folks typically do not care about. These are the `"indirect"` dependencies. They are listed explicitly so that (1) builds are reproducible and (2) you can easily review the quantity and quality of dependencies.
61 |
62 | **Note:** We plan to eventually have a screen in `reactor` that helps add, remove, and upgrade packages. It can sometimes be tricky to keep all of the constraints happy, so we think having a UI will help a lot. If you get into trouble in the meantime, adding things back one-by-one often helps, and I hope you do not get into trouble!
63 |
64 |
65 |
66 |
67 | ## `"test-dependencies"`
68 |
69 | All the packages that you use in `tests/` with `elm-test` but not in the application you actually want to ship. This also uses exact versions to make tests more reliable.
70 |
--------------------------------------------------------------------------------
/docs/elm.json/package.md:
--------------------------------------------------------------------------------
1 | # `elm.json` for packages
2 |
3 | This is roughly `elm.json` for the `elm/json` package:
4 |
5 | ```json
6 | {
7 | "type": "package",
8 | "name": "elm/json",
9 | "summary": "Encode and decode JSON values",
10 | "license": "BSD-3-Clause",
11 | "version": "1.0.0",
12 | "exposed-modules": [
13 | "Json.Decode",
14 | "Json.Encode"
15 | ],
16 | "elm-version": "0.19.0 <= v < 0.20.0",
17 | "dependencies": {
18 | "elm/core": "1.0.0 <= v < 2.0.0"
19 | },
20 | "test-dependencies": {}
21 | }
22 | ```
23 |
24 |
25 |
26 |
27 | ## `"type"`
28 |
29 | Either `"application"` or `"package"`. All the other fields are based on this choice.
30 |
31 |
32 |
33 |
34 | ## `"name"`
35 |
36 | The name of a GitHub repo like `"elm-lang/core"` or `"rtfeldman/elm-css"`.
37 |
38 | > **Note:** We currently only support GitHub repos to ensure that there are no author name collisions. This seems like a pretty tricky problem to solve in a pleasant way. For example, do we have to keep an author name registry and give them out as we see them? But if someone is the same person on two platforms? And how to make this all happen in a way this is really nice for typical Elm users? Etc. So adding other hosting endpoints is harder than it sounds.
39 |
40 |
41 |
42 |
43 | ## `"summary"`
44 |
45 | A short summary that will appear on [`package.elm-lang.org`](https://package.elm-lang.org/) that describes what the package is for. Must be under 80 characters.
46 |
47 |
48 |
49 |
50 | ## `"license"`
51 |
52 | An OSI approved SPDX code like `"BSD-3-Clause"` or `"MIT"`. These are the two most common licenses in the Elm ecosystem, and BSD-3-Clause is a good default. But you can see the full list of options [here](https://spdx.org/licenses/).
53 |
54 |
55 |
56 |
57 | ## `"version"`
58 |
59 | All packages start at `"1.0.0"` and from there, Elm automatically enforces semantic versioning by comparing API changes.
60 |
61 | So if you make a PATCH change and call `elm bump` it will update you to `"1.0.1"`. And if you then decide to remove a function (a MAJOR change) and call `elm bump` it will update you to `"2.0.0"`. Etc.
62 |
63 |
64 |
65 |
66 | ## `"exposed-modules"`
67 |
68 | A list of modules that will be exposed to people using your package. The order you list them will be the order they appear on [`package.elm-lang.org`](https://package.elm-lang.org/).
69 |
70 | **Note:** If you have five or more modules, you can use a labelled list like [this](https://github.com/elm-lang/core/blob/master/elm.json). We show the labels on the package website to help people sort through larger packages with distinct categories. Labels must be under 20 characters.
71 |
72 |
73 |
74 |
75 | ## `"elm-version"`
76 |
77 | The range of Elm compilers that work with your package. Right now `"0.19.0 <= v < 0.20.0"` is always what you want for this.
78 |
79 |
80 |
81 |
82 | ## `"dependencies"`
83 |
84 | A list of packages that you depend upon. In each application, there can only be one version of each package, so wide ranges are great. Fewer dependencies is even better though!
85 |
86 | > **Note:** Dependency ranges should only express _tested_ ranges. It is not nice to use optimistic ranges and end up causing build failures for your users down the line. Eventually we would like to have an automated system that tries to build and test packages as new packages come out. If it all works, we could send a PR to the author widening the range.
87 |
88 |
89 |
90 |
91 | ## `"test-dependencies"`
92 |
93 | Dependencies that are only used in the `tests/` directory by `elm test`. Values from these packages will not appear in any final build artifacts.
94 |
--------------------------------------------------------------------------------
/docs/upgrade-instructions/0.19.1.md:
--------------------------------------------------------------------------------
1 | # Upgrading to 0.19.1
2 |
3 | **There are no language changes**, so once you swap to `"elm-version": "0.19.1"` in your `elm.json`, most users should be able to proceed without any further code changes. **You may run into a handful of bugfixes though!** These cases are outlined below!
4 |
5 |
6 |
7 |
8 | ## Improvements
9 |
10 | - Parse error message quality (like [this](https://github.com/elm/error-message-catalog/issues/255) and [this](https://github.com/elm/error-message-catalog/issues/225))
11 | - Faster compilation, especially for incremental compiles
12 | - Uses filelocks so that cached files are not corrupted when plugins run `elm make` multiple times on the same project at the same time. (Still worth avoiding that though!)
13 | - More intuitive multiline declarations in REPL
14 | - Various bug fixes (e.g. `--debug`, `x /= 0`, `type Height = Height Float` in `--optimize`)
15 |
16 |
17 |
18 |
19 | ## Detectable Bug Fixes
20 |
21 | There are three known cases where code that compiled with 0.19.0 will not compile with 0.19.1 due to bug fixes:
22 |
23 |
24 | ### 1. Ambiguous Imports
25 |
26 | Say you have an import like this:
27 |
28 | ```elm
29 | import Html exposing (min)
30 | import Regex exposing (never)
31 |
32 | x = min
33 | y = never
34 | ```
35 |
36 | These should be reported as ambiguous usages since the names are also exposed by `Basics`, but there was a regression in 0.19.0 described [here](https://github.com/elm/compiler/issues/1945) such that they weren't caught in specific circumstances.
37 |
38 | The fix is to use a qualified name like `Html.min` or `Regex.never` to make it unambiguous.
39 |
40 | We found a couple instances of this in packages and have submitted PRs to the relevant authors in August 2019. You may run into this in your own code as well.
41 |
42 | For more details on why this is considered a regression, check out the details [here](https://github.com/elm/compiler/issues/1945#issuecomment-507871919) or try it in 0.18.0 to see how it worked before.
43 |
44 |
45 | ### 2. Tabs in Comments
46 |
47 | The 0.19.0 binaries did not catch tab characters in comments. The new parser is better at checking for tabs, so it will object when it finds these.
48 |
49 | Again, we found this in some packages and reached out to the relevant authors with PRs so patches would be published before the 0.19.1 release.
50 |
51 |
52 | ### 3. Port Module with no Ports
53 |
54 | If you have any files that start with:
55 |
56 | ```elm
57 | port module Main exposing (..)
58 | ```
59 |
60 | But they do not actually have any `port` declarations, the 0.19.1 binary will ask you to switch to a normal module declaration like `module Main exposing (..)`
61 |
--------------------------------------------------------------------------------
/hints/comparing-custom-types.md:
--------------------------------------------------------------------------------
1 | # Comparing Custom Types
2 |
3 | The built-in comparison operators work on a fixed set of types, like `Int` and `String`. That covers a lot of cases, but what happens when you want to compare custom types?
4 |
5 | This page aims to catalog these scenarios and offer alternative paths that can get you unstuck.
6 |
7 |
8 | ## Wrapped Types
9 |
10 | It is common to try to get some extra type safety by creating really simple custom types:
11 |
12 | ```elm
13 | type Id = Id Int
14 | type Age = Age Int
15 |
16 | type Comment = Comment String
17 | type Description = Description String
18 | ```
19 |
20 | By wrapping the primitive values like this, the type system can now help you make sure that you never mix up a `Id` and an `Age`. Those are different types! This trick is extra cool because it has no runtime cost in `--optimize` mode. The compiler can just use an `Int` or `String` directly when you use that flag!
21 |
22 | The problem arises when you want to use a `Id` as a key in a dictionary. This is a totally reasonable thing to do, but the current version of Elm cannot handle this scenario.
23 |
24 | Instead of creating a `Dict Id Info` type, one thing you can do is create a custom data structure like this:
25 |
26 | ```elm
27 | module User exposing (Id, Table, empty, get, add)
28 |
29 | import Dict exposing (Dict)
30 |
31 |
32 | -- USER
33 |
34 | type Id = Id Int
35 |
36 |
37 | -- TABLE
38 |
39 | type Table info =
40 | Table Int (Dict Int info)
41 |
42 | empty : Table info
43 | empty =
44 | Table 0 Dict.empty
45 |
46 | get : Id -> Table info -> Maybe info
47 | get (Id id) (Table _ dict) =
48 | Dict.get id dict
49 |
50 | add : info -> Table info -> (Table info, Id)
51 | add info (Table nextId dict) =
52 | ( Table (nextId + 1) (Dict.insert nextId info dict)
53 | , Id nextId
54 | )
55 | ```
56 |
57 | There are a couple nice things about this approach:
58 |
59 | 1. The only way to get a new `User.Id` is to `add` information to a `User.Table`.
60 | 2. All the operations on a `User.Table` are explicit. Does it make sense to remove users? To merge two tables together? Are there any special details to consider in those cases? This will always be captured explicitly in the interface of the `User` module.
61 | 3. If you ever want to switch the internal representation from `Dict` to `Array` or something else, it is no problem. All the changes will be within the `User` module.
62 |
63 | So while this approach is not as convenient as using a `Dict` directly, it has some benefits of its own that can be helpful in some cases.
64 |
65 |
66 | ## Enumerations to Ints
67 |
68 | Say you need to define a `trafficLightToInt` function:
69 |
70 | ```elm
71 | type TrafficLight = Green | Yellow | Red
72 |
73 | trafficLightToInt : TrafficLight -> Int
74 | trafficLightToInt trafficLight =
75 | ???
76 | ```
77 |
78 | We have heard that some people would prefer to use a dictionary for this sort of thing. That way you do not need to write the numbers yourself, they can be generated such that you never have a typo.
79 |
80 | I would recommend using a `case` expression though:
81 |
82 | ```elm
83 | type TrafficLight = Green | Yellow | Red
84 |
85 | trafficLightToInt : TrafficLight -> Int
86 | trafficLightToInt trafficLight =
87 | case trafficLight of
88 | Green -> 1
89 | Yellow -> 2
90 | Red -> 3
91 | ```
92 |
93 | This is really straight-forward while avoiding questions like “is `Green` less than or greater than `Red`?”
94 |
95 |
96 | ## Something else?
97 |
98 | If you have some other situation, please tell us about it [here](https://github.com/elm/error-message-catalog/issues). That is a log of error messages that can be improved, and we can use the particulars of your scenario to add more advice on this page!
99 |
--------------------------------------------------------------------------------
/hints/comparing-records.md:
--------------------------------------------------------------------------------
1 | # Comparing Records
2 |
3 | The built-in comparison operators work on a fixed set of types, like `Int` and `String`. That covers a lot of cases, but what happens when you want to compare records?
4 |
5 | This page aims to catalog these scenarios and offer alternative paths that can get you unstuck.
6 |
7 |
8 | ## Sorting Records
9 |
10 | Say we want a `view` function that can show a list of students sorted by different characteristics.
11 |
12 | We could create something like this:
13 |
14 | ```elm
15 | import Html exposing (..)
16 |
17 | type alias Student =
18 | { name : String
19 | , age : Int
20 | , gpa : Float
21 | }
22 |
23 | type Order = Name | Age | GPA
24 |
25 | viewStudents : Order -> List Student -> Html msg
26 | viewStudents order students =
27 | let
28 | orderlyStudents =
29 | case order of
30 | Name -> List.sortBy .name students
31 | Age -> List.sortBy .age students
32 | GPA -> List.sortBy .gpa students
33 | in
34 | ul [] (List.map viewStudent orderlyStudents)
35 |
36 | viewStudent : Student -> Html msg
37 | viewStudent student =
38 | li [] [ text student.name ]
39 | ```
40 |
41 | If you are worried about the performance of changing the order or updating information about particular students, you can start using the [`Html.Lazy`](https://package.elm-lang.org/packages/elm/html/latest/Html-Lazy) and [`Html.Keyed`](https://package.elm-lang.org/packages/elm/html/latest/Html-Keyed) modules. The updated code would look something like this:
42 |
43 | ```elm
44 | import Html exposing (..)
45 | import Html.Lazy exposing (lazy)
46 | import Html.Keyed as Keyed
47 |
48 | type Order = Name | Age | GPA
49 |
50 | type alias Student =
51 | { name : String
52 | , age : Int
53 | , gpa : Float
54 | }
55 |
56 | viewStudents : Order -> List Student -> Html msg
57 | viewStudents order students =
58 | let
59 | orderlyStudents =
60 | case order of
61 | Name -> List.sortBy .name students
62 | Age -> List.sortBy .age students
63 | GPA -> List.sortBy .gpa students
64 | in
65 | Keyed.ul [] (List.map viewKeyedStudent orderlyStudents)
66 |
67 | viewKeyedStudent : Student -> (String, Html msg)
68 | viewKeyedStudent student =
69 | ( student.name, lazy viewStudent student )
70 |
71 | viewStudent : Student -> Html msg
72 | viewStudent student =
73 | li [] [ text student.name ]
74 | ```
75 |
76 | By using `Keyed.ul` we help the renderer move the DOM nodes around based on their key. This makes it much cheaper to reorder a bunch of students. And by using `lazy` we help the renderer skip a bunch of work. If the `Student` is the same as last time, the render can skip over it.
77 |
78 | > **Note:** Some people are skeptical of having logic like this in `view` functions, but I think the alternative (maintaining sort order in your `Model`) has some serious downsides. Say a colleague is adding a message to `Add` students, but they do not know about the sort order rules needed for presentation. Bug! So in this alternate design, you must diligently test your `update` function to make sure that no message disturbs the sort order. This is bound to lead to bugs over time!
79 | >
80 | > With all the optimizations possible with `Html.Lazy` and `Html.Keyed`, I would always be inclined to work on optimizing my `view` functions rather than making my `update` functions more complicated and error prone.
81 |
82 |
83 | ## Something else?
84 |
85 | If you have some other situation, please tell us about it [here](https://github.com/elm/error-message-catalog/issues). That is a log of error messages that can be improved, and we can use the particulars of your scenario to add more advice on this page!
86 |
--------------------------------------------------------------------------------
/hints/implicit-casts.md:
--------------------------------------------------------------------------------
1 |
2 | # Implicit Casts
3 |
4 | Many languages automatically convert from `Int` to `Float` when they think it is necessary. This conversion is often called an [implicit cast](https://en.wikipedia.org/wiki/Type_conversion).
5 |
6 | Languages that will add in implicit casts for addition include:
7 |
8 | - JavaScript
9 | - Python
10 | - Ruby
11 | - C
12 | - C++
13 | - C#
14 | - Java
15 | - Scala
16 |
17 | These languages generally agree that an `Int` may be implicitly cast to a `Float` when necessary. So everyone is doing it, why not Elm?!
18 |
19 | ## Type Inference + Implicit Casts
20 |
21 | Elm comes from the ML-family of languages. Languages in the ML-family that **never** do implicit casts include:
22 |
23 | - Standard ML
24 | - OCaml
25 | - Elm
26 | - F#
27 | - Haskell
28 |
29 | Why would so many languages from this lineage require explicit conversions though?
30 |
31 | Well, we have to go back to the 1970s for some background. J. Roger Hindley and Robin Milner independently discovered an algorithm that could _efficiently_ figure out the type of everything in your program without any type annotations. Type Inference! Every ML-family language has some variation of this algorithm at the center of its design.
32 |
33 | For decades, the problem was that nobody could figure out how to combine type inference with implicit casts AND make the resulting algorithm efficient enough for daily use. As far as I know, Scala was the first widely known language to figure out how to combine these two things! Its creator, Martin Odersky did a lot of work on combining type inference and subtyping to make this possible.
34 |
35 | So for any ML-family language designed before Scala, it is safe to assume that implicit conversions just was not an option. Okay, but what about Elm?! It comes after Scala, so why not do it like them?!
36 |
37 | 1. You pay performance cost to mix type inference and implicit conversions. At least as far as anyone knows, it defeats an optimization that is crucial to getting _reliably_ good performance. It is fine in most cases, but it can be a real issue in very large code bases.
38 |
39 | 2. Based on experience reports from Scala users, it seemed like the convenience was not worth the hidden cost. Yes, you can convert `n` in `(n + 1.5)` and everything is nice, but when you are in larger programs that are sparsely annotated, it can be quite difficult to figure out what is going on.
40 |
41 | This user data may be confounded by the fact that Scala allows quite extensive conversions, not just from `Int` to `Float`, but I think it is worth taking seriously nonetheless. So it is _possible_, but it has tradeoffs.
42 |
43 |
44 | ## Conclusion
45 |
46 | First, based on the landscape of design possibilities, it seems like requiring _explicit_ conversions is a pretty nice balance. We can have type inference, it can produce friendly error messages, the algorithm is snappy, and an unintended implicit cast will not flow hundreds of lines before manifesting to the user.
47 |
48 | Second, Elm very much favors explicit code, so this also fits in with the overall spirit of the language and libraries.
49 |
50 | I hope that clarifies why you have to add those `toFloat` and `round` functions! It definitely can take some getting used to, but there are tons of folks who get past that acclimation period and really love the tradeoffs!
51 |
--------------------------------------------------------------------------------
/hints/infinite-type.md:
--------------------------------------------------------------------------------
1 |
2 | # Hints for Infinite Types
3 |
4 | Infinite types are probably the trickiest kind of bugs to track down. **Writing down type annotations is usually the fastest way to figure them out.** Let's work through an example to get a feel for how these errors usually work though!
5 |
6 |
7 | ## Example
8 |
9 | A common way to get an infinite type error is very small typos. For example, do you see the problem in the following code?
10 |
11 | ```elm
12 | incrementNumbers list =
13 | List.map incrementNumbers list
14 |
15 | incrementNumber n =
16 | n + 1
17 | ```
18 |
19 | The issue is that `incrementNumbers` calls itself, not the `incrementNumber` function defined below. So there is an extra `s` in this program! Let's focus on that:
20 |
21 | ```elm
22 | incrementNumbers list =
23 | List.map incrementNumbers list -- BUG extra `s` makes this self-recursive
24 | ```
25 |
26 | Now the compiler does not know that anything is wrong yet. It just tries to figure out the types like normal. It knows that `incrementNumbers` is a function. The definition uses `List.map` so we can deduce that `list : List t1` and the result of this function call should be some other `List t2`. This also means that `incrementNumbers : List t1 -> List t2`.
27 |
28 | The issue is that `List.map` uses `incrementNumbers` on `list`! That means that each element of `list` (which has type `t1`) must be fed into `incrementNumbers` (which takes `List t1`)
29 |
30 | That means that `t1 = List t1`, which is an infinite type! If we start expanding this, we get `List (List (List (List (List ...))))` out to infinity!
31 |
32 | The point is mainly that we are in a confusing situation. The types are confusing. This explanation is confusing. The compiler is confused. It is a bad time. But luckily, the more type annotations you add, the better chance there is that you and the compiler can figure things out! So say we change our definition to:
33 |
34 | ```elm
35 | incrementNumbers : List Int -> List Int
36 | incrementNumbers list =
37 | List.map incrementNumbers list -- STILL HAS BUG
38 | ```
39 |
40 | Now we are going to get a pretty normal type error. Hey, you said that each element in the `list` is an `Int` but I cannot feed that into a `List Int -> List Int` function! Something like that.
41 |
42 | In summary, the root issue is often some small typo, and the best way out is to start adding type annotations on everything!
43 |
--------------------------------------------------------------------------------
/hints/init.md:
--------------------------------------------------------------------------------
1 |
2 | # Creating an Elm project
3 |
4 | The main goal of `elm init` is to get you to this page!
5 |
6 | It just creates an `elm.json` file and a `src/` directory for your code.
7 |
8 |
9 | ## What is `elm.json`?
10 |
11 | This file describes your project. It lists all of the packages you depend upon, so it will say the particular version of [`elm/core`](https://package.elm-lang.org/packages/elm/core/latest/) and [`elm/html`](https://package.elm-lang.org/packages/elm/html/latest/) that you are using. It makes builds reproducible! You can read a bit more about it [here](https://github.com/elm/compiler/blob/master/docs/elm.json/application.md).
12 |
13 | You should generally not edit it by hand. It is better to add new dependencies with commands like `elm install elm/http` or `elm install elm/json`.
14 |
15 |
16 | ## What goes in `src/`?
17 |
18 | This is where all of your Elm files live. It is best to start with a file called `src/Main.elm`. As you work through [the official guide](https://guide.elm-lang.org/), you can put the code examples in that `src/Main.elm` file.
19 |
20 |
21 | ## How do I compile it?
22 |
23 | Run `elm reactor` in your project. Now you can go to [`http://localhost:8000`](http://localhost:8000) and browse through all the files in your project. If you navigate to `.elm` files, it will compile them for you!
24 |
25 | If you want to do things more manually, you can run `elm make src/Main.elm` and it will produce an `index.html` file that you can look at in your browser.
26 |
27 |
28 | ## How do I structure my directories?
29 |
30 | Many folks get anxious about their project structure. “If I get it wrong, I am doomed!” This anxiety makes sense in languages where refactoring is risky, but Elm is not one of those languages!
31 |
32 | So we recommend that newcomers staying in one file until you get into the 600 to 1000 range. Push out of your comfort zone. Having the experience of being fine in large files will help you understand the boundaries in Elm, rather than just defaulting to the boundaries you learned in another language.
33 |
34 | The talk [The Life of a File](https://youtu.be/XpDsk374LDE) gets into this a lot more. The advice about building modules around a specific [custom type](https://guide.elm-lang.org/types/custom_types.html) is particularly important! You will see that emphasized a lot as you work through the official guide.
35 |
36 |
37 | ## How do I write tests?
38 |
39 | Elm will catch a bunch of errors statically, and I think it is worth skipping tests at first to get a feeling for when tests will actually help you _in Elm_.
40 |
41 | From there, we have a great testing package called [`elm-explorations/test`](https://github.com/elm-explorations/test) that can help you out! It is particularly helpful for teams working on a large codebase. When you are editing code you have never seen before, tests can capture additional details and constraints that are not otherwise apparent!
42 |
43 |
44 | ## How do I start fancier projects?
45 |
46 | I wanted `elm init` to generate as little code as possible. It is mainly meant to get you to this page! If you would like a more elaborate starting point, I recommend starting projects with commands like these:
47 |
48 | ```bash
49 | git clone https://github.com/evancz/elm-todomvc.git
50 | git clone https://github.com/rtfeldman/elm-spa-example.git
51 | ```
52 |
53 | The idea is that Elm projects should be so simple that nobody needs a tool to generate a bunch of stuff. This also captures the fact that project structure _should_ evolve organically as your application develops, never ending up exactly the same as other projects.
54 |
55 | But if you have something particular you want, I recommend creating your own starter recipe and using `git clone` when you start new projects. That way (1) you can get exactly what you want and (2) we do not end up with a complex `elm init` that ends up being confusing for beginners!
56 |
--------------------------------------------------------------------------------
/hints/missing-patterns.md:
--------------------------------------------------------------------------------
1 |
2 | # Hints for Missing Patterns
3 |
4 | Elm checks to make sure that all possible inputs to a function or `case` are handled. This gives us the guarantee that no Elm code is ever going to crash because data had an unexpected shape.
5 |
6 | There are a couple techniques for making this work for you in every scenario.
7 |
8 |
9 | ## The danger of wildcard patterns
10 |
11 | A common scenario is that you want to add a tag to a custom type that is used in a bunch of places. For example, maybe you are working different variations of users in a chat room:
12 |
13 | ```elm
14 | type User
15 | = Regular String Int
16 | | Anonymous
17 |
18 | toName : User -> String
19 | toName user =
20 | case user of
21 | Regular name _ ->
22 | name
23 |
24 | _ ->
25 | "anonymous"
26 | ```
27 |
28 | Notice the wildcard pattern in `toName`. This will hurt us! Say we add a `Visitor String` variant to `User` at some point. Now we have a bug that visitor names are reported as `"anonymous"`, and the compiler cannot help us!
29 |
30 | So instead, it is better to explicitly list all possible variants, like this:
31 |
32 | ```elm
33 | type User
34 | = Regular String Int
35 | | Visitor String
36 | | Anonymous
37 |
38 | toName : User -> String
39 | toName user =
40 | case user of
41 | Regular name _ ->
42 | name
43 |
44 | Anonymous ->
45 | "anonymous"
46 | ```
47 |
48 | Now the compiler will say "hey, what should `toName` do when it sees a `Visitor`?" This is a tiny bit of extra work, but it is very worth it!
49 |
50 |
51 | ## I want to go fast!
52 |
53 | Imagine that the `User` type appears in 20 or 30 functions across your project. When we add a `Visitor` variant, the compiler points out all the places that need to be updated. That is very convenient, but in a big project, maybe you want to get through it extra quickly.
54 |
55 | In that case, it can be helpful to use [`Debug.todo`](https://package.elm-lang.org/packages/elm-lang/core/latest/Debug#todo) to leave some code incomplete:
56 |
57 | ```elm
58 | type User
59 | = Regular String Int
60 | | Visitor String
61 | | Anonymous
62 |
63 | toName : User -> String
64 | toName user =
65 | case user of
66 | Regular name _ ->
67 | name
68 |
69 | Visitor _ ->
70 | Debug.todo "give the visitor name"
71 |
72 | Anonymous ->
73 | "anonymous"
74 |
75 | -- and maybe a bunch of other things
76 | ```
77 |
78 | In this case it is easier to just write the implementation, but the point is that on more complex functions, you can put things off a bit.
79 |
80 | The Elm compiler is actually aware of `Debug.todo` so when it sees it in a `case` like this, it will crash with a bunch of helpful information. It will tell you:
81 |
82 | 1. The name of the module that contains the code.
83 | 2. The line numbers of the `case` containing the TODO.
84 | 3. The particular value that led to this TODO.
85 |
86 | From that information you have a pretty good idea of what went wrong and can go fix it.
87 |
88 | I tend to use `Debug.todo` as the message when my goal is to go quick because it makes it easy to go and find all remaining todos in my code before a release.
89 |
90 |
91 | ## A list that definitely is not empty
92 |
93 | This can come up from time to time, but Elm **will not** let you write code like this:
94 |
95 | ```elm
96 | last : List a -> a
97 | last list =
98 | case list of
99 | [x] ->
100 | x
101 |
102 | _ :: rest ->
103 | last rest
104 | ```
105 |
106 | This is no good. It does not handle the empty list. There are two ways to handle this. One is to make the function return a `Maybe` like this:
107 |
108 | ```elm
109 | last : List a -> Maybe a
110 | last list =
111 | case list of
112 | [] ->
113 | Nothing
114 |
115 | [x] ->
116 | Just x
117 |
118 | _ :: rest ->
119 | last rest
120 | ```
121 |
122 | This is nice because it lets users know that there might be a failure, so they can recover from it however they want.
123 |
124 | The other option is to “unroll the list” one level to ensure that no one can ever provide an empty list in the first place:
125 |
126 | ```elm
127 | last : a -> List a -> a
128 | last first rest =
129 | case rest of
130 | [] ->
131 | first
132 |
133 | newFirst :: newRest ->
134 | last newFirst newRest
135 | ```
136 |
137 | By demanding the first element of the list as an argument, it becomes impossible to call this function if you have an empty list!
138 |
139 | This “unroll the list” trick is quite useful. I recommend using it directly, not through some external library. It is nothing special. Just a useful idea!
140 |
--------------------------------------------------------------------------------
/hints/optimize.md:
--------------------------------------------------------------------------------
1 |
2 | # How to optimize Elm code
3 |
4 | When you are serving a website, there are two kinds of optimizations you want to do:
5 |
6 | 1. **Asset Size** — How can we send as few bits as possible?
7 | 2. **Performance** — How can those bits run as quickly as possible?
8 |
9 | It turns out that Elm does really well on both! We have [very small assets](https://elm-lang.org/news/small-assets-without-the-headache) and [very fast code](https://elm-lang.org/news/blazing-fast-html-round-two) when compared to the popular alternatives.
10 |
11 | Okay, but how do we get those numbers?
12 |
13 |
14 | ## Instructions
15 |
16 | Step one is to compile with the `--optimize` flag. This does things like shortening record field names and unboxing values.
17 |
18 | Step two is to call `uglifyjs` with a bunch of special flags. The flags unlock optimizations that are unreliable in normal JS code, but because Elm does not have side-effects, they work fine for us!
19 |
20 | Putting those together, here is how I would optimize `src/Main.elm` with two terminal commands:
21 |
22 | ```bash
23 | elm make src/Main.elm --optimize --output=elm.js
24 | uglifyjs elm.js --compress "pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe" | uglifyjs --mangle --output elm.min.js
25 | ```
26 |
27 | After this you will have an `elm.js` and a significantly smaller `elm.min.js` file!
28 |
29 | **Note 1:** `uglifyjs` is called twice there. First to `--compress` and second to `--mangle`. This is necessary! Otherwise `uglifyjs` will ignore our `pure_funcs` flag.
30 |
31 | **Note 2:** If the `uglifyjs` command is not available in your terminal, you can run the command `npm install uglify-js --global` to download it. You probably already have `npm` from getting `elm repl` working, but if not, it is bundled with [nodejs](https://nodejs.org/).
32 |
33 | ## Scripts
34 |
35 | It is hard to remember all that, so it is probably a good idea to write a script that does it.
36 |
37 | I would maybe want to run `./optimize.sh src/Main.elm` and get out `elm.js` and `elm.min.js`, so on Mac or Linux, I would make a script called `optimize.sh` like this:
38 |
39 | ```bash
40 | #!/bin/sh
41 |
42 | set -e
43 |
44 | js="elm.js"
45 | min="elm.min.js"
46 |
47 | elm make --optimize --output=$js $@
48 |
49 | uglifyjs $js --compress "pure_funcs=[F2,F3,F4,F5,F6,F7,F8,F9,A2,A3,A4,A5,A6,A7,A8,A9],pure_getters,keep_fargs=false,unsafe_comps,unsafe" | uglifyjs --mangle --output $min
50 |
51 | echo "Initial size: $(cat $js | wc -c) bytes ($js)"
52 | echo "Minified size:$(cat $min | wc -c) bytes ($min)"
53 | echo "Gzipped size: $(cat $min | gzip -c | wc -c) bytes"
54 | ```
55 |
56 | It also prints out all the asset sizes for you! Your server should be configured to gzip the assets it sends, so the last line is telling you how many bytes would _actually_ get sent to the user.
57 |
58 | Again, the important commands are `elm` and `uglifyjs` which work on any platform, so it should not be too tough to do something similar on Windows.
59 |
60 |
--------------------------------------------------------------------------------
/hints/port-modules.md:
--------------------------------------------------------------------------------
1 |
2 | # No Ports in Packages
3 |
4 | The package ecosystem is one of the most important parts of Elm. Right now, our ecosystem has some compelling benefits:
5 |
6 | - There are many obvious default packages that work well.
7 | - Adding dependencies cannot introduce runtime exceptions.
8 | - Patch changes cannot lead to surprise build failures.
9 |
10 | These are really important factors if you want to *quickly* create *reliable* applications. The Elm community thinks this is valuable.
11 |
12 | Other communities think that the *number* of packages is a better measure of ecosystem health. That is a fine metric to use, but it is not the one we use for Elm. We would rather have 50 great packages than 100k packages of wildly varying quality.
13 |
14 |
15 | ## So what about ports?
16 |
17 | Imagine you install a new package that claims to support `localStorage`. You get it set up, working through any compile errors. You run it, but it does not seem to work! After trying to figure it out for hours, you realize there is some poorly documented `port` to hook up...
18 |
19 | Okay, now you need to hook up some JavaScript code. Is that JS file in the Elm package? Or is it on `npm`? Wait, what version on `npm` though? And is this patch version going to work as well? Also, how does this file fit into my build process? And assuming we get through all that, maybe the `port` has the same name as one of the ports in your project. Or it clashes with a `port` name in another package.
20 |
21 | **Suddenly adding dependencies is much more complicated and risky!** An experienced developer would always check for ports up front, spending a bunch of time manually classifying unacceptable packages. Most people would not know to do that and learn all the pitfalls through personal experience, ultimately spending even *more* time than the person who defensively checks to avoid these issues.
22 |
23 | So “ports in packages” would impose an enormous cost on application developers, and in the end, we would have a less reliable package ecosystem overall.
24 |
25 |
26 | ## Conclusion
27 |
28 | Our wager with the Elm package ecosystem is that it is better to get a package *right* than to get it *right now*. So while we could use “ports in packages” as a way to get twenty `localStorage` packages of varying quality *right now*, we are choosing not to go that route. Instead we ask that developers use ports directly in their application code, getting the same result a different way.
29 |
30 | Now this may not be the right choice for your particular project, and that is okay! We will be expanding our core libraries over time, as explained [here](https://github.com/elm-lang/projects/blob/master/roadmap.md#where-is-the-localstorage-package), and we hope you will circle back later to see if Elm has grown into a better fit!
31 |
32 | If you have more questions about this choice or what it means for your application, please come ask in [the Elm slack](http://elmlang.herokuapp.com/). Folks are friendly and happy to help out! Chances are that a `port` in your application will work great for your case once you learn more about how they are meant to be used.
33 |
--------------------------------------------------------------------------------
/hints/repl.md:
--------------------------------------------------------------------------------
1 |
2 | # REPL
3 |
4 | The REPL lets you interact with Elm values and functions in your terminal.
5 |
6 |
7 | ## Use
8 |
9 | You can type in expressions, definitions, custom types, and module imports using normal Elm syntax.
10 |
11 | ```elm
12 | > 1 + 1
13 | 2 : number
14 |
15 | > "hello" ++ "world"
16 | "helloworld" : String
17 | ```
18 |
19 | The same can be done with definitions and custom types:
20 |
21 | ```elm
22 | > fortyTwo = 42
23 | 42 : number
24 |
25 | > increment n = n + 1
26 | : number -> number
27 |
28 | > increment 41
29 | 42 : number
30 |
31 | > factorial n =
32 | | if n < 1 then
33 | | 1
34 | | else
35 | | n * factorial (n-1)
36 | |
37 | : number -> number
38 |
39 | > factorial 5
40 | 120 : number
41 |
42 | > type User
43 | | = Regular String
44 | | | Visitor String
45 | |
46 |
47 | > case Regular "Tom" of
48 | | Regular name -> "Hey again!"
49 | | Visitor name -> "Nice to meet you!"
50 | |
51 | "Hey again!" : String
52 | ```
53 |
54 | When you run `elm repl` in a project with an [`elm.json`](https://github.com/elm/compiler/blob/master/docs/elm.json/application.md) file, you can import any module available in the project. So if your project has an `elm/html` dependency, you could say:
55 |
56 | ```elm
57 | > import Html exposing (Html)
58 |
59 | > Html.text "hello"
60 | : Html msg
61 |
62 | > Html.text
63 | : String -> Html msg
64 | ```
65 |
66 | If you create a module in your project named `MyThing` in your project, you can say `import MyThing` in the REPL as well. Any module that is accessible in your project should be accessible in the REPL.
67 |
68 |
69 | ## Exit
70 |
71 | To exit the REPL, you can type `:exit`.
72 |
73 | You can also press `ctrl-d` or `ctrl-c` on some platforms.
74 |
--------------------------------------------------------------------------------
/hints/shadowing.md:
--------------------------------------------------------------------------------
1 |
2 | # Variable Shadowing
3 |
4 | Variable shadowing is when you define the same variable name twice in an ambiguous way. Here is a pretty reasonable use of shadowing:
5 |
6 | ```elm
7 | viewName : Maybe String -> Html msg
8 | viewName name =
9 | case name of
10 | Nothing ->
11 | ...
12 |
13 | Just name ->
14 | ...
15 | ```
16 |
17 | I define a `name` with type `Maybe String` and then in that second branch, I define a `name` that is a `String`. Now that there are two `name` values, it is not 100% obvious which one you want in that second branch.
18 |
19 | Most linters produce warnings on variable shadowing, so Elm makes “best practices” the default. Just rename the first one to `maybeName` and move on.
20 |
21 | This choice is relatively uncommon in programming languages though, so I want to provide the reasoning behind it.
22 |
23 |
24 | ## The Cost of Shadowing
25 |
26 | The code snippet from above is the best case scenario for variable shadowing. It is pretty clear really. But that is because it is a fake example. It does not even compile.
27 |
28 | In a large module that is evolving over time, this is going to cause bugs in a very predictable way. You will have two definitions, separated by hundreds of lines. For example:
29 |
30 | ```elm
31 | name : String
32 | name =
33 | "Tom"
34 |
35 | -- hundreds of lines
36 |
37 | viewName : String -> Html msg
38 | viewName name =
39 | ... name ... name ... name ...
40 | ```
41 |
42 | Okay, so the `viewName` function has an argument `name` and it uses it three times. Maybe the `viewName` function is 50 lines long in total, so those uses are not totally easy to see. This is fine so far, but say your colleague comes along five months later and wants to support first and last names. They refactor the code like this:
43 |
44 | ```elm
45 | viewName : String -> String -> Html msg
46 | viewName firstName lastName =
47 | ... name ... name ... name ...
48 | ```
49 |
50 | The code compiles, but it does not work as intended. They forgot to change all the uses of `name`, and because it shadows the top-level `name` value, it always shows up as `"Tom"`. It is a simple mistake, but it is always the last thing I think of.
51 |
52 | > Is the data being fetched properly? Let me log all of the JSON requests. Maybe the JSON decoders are messed up? Hmm. Maybe someone is transforming the name in a bad way at some point? Let me check my `update` code.
53 |
54 | Basically, a bunch of time gets wasted on something that could easily be detected by the compiler. But this bug is rare, right?
55 |
56 |
57 | ## Aggregate Cost
58 |
59 | Thinking of a unique and helpful name takes some extra time. Maybe 30 seconds. But it means that:
60 |
61 | 1. Your code is easier to read and understand later on. So you spend 30 seconds once `O(1)` rather than spending 10 seconds each time someone reads that code in the future `O(n)`.
62 |
63 | 2. The tricky shadowing bug described above is impossible. Say there is a 5% chance that any given edit produces a shadowing bug, and that resolving that shadowing bug takes one hour. That means the expected time for each edit increases by three minutes.
64 |
65 | If you are still skeptical, I encourage you can play around with the number of edits, time costs, and probabilities here. When shadowing is not allowed, the resulting overhead for the entire lifetime of the code is the 30 seconds it takes to pick a better name, so that is what you need to beat!
66 |
67 |
68 | ## Summary
69 |
70 | Without shadowing, the code easier to read and folks spend less time on pointless debugging. The net outcome is that folks have more time to make something wonderful with Elm!
71 |
--------------------------------------------------------------------------------
/hints/tuples.md:
--------------------------------------------------------------------------------
1 |
2 | # From Tuples to Records
3 |
4 | The largest tuple possible in Elm has three entries. Once you get to four, it is best to make a record with named entries.
5 |
6 | For example, it is _conceivable_ to represent a rectangle as four numbers like `(10,10,100,100)` but it would be more self-documenting to use a record like this:
7 |
8 | ```elm
9 | type alias Rectangle =
10 | { x : Float
11 | , y : Float
12 | , width : Float
13 | , height : Float
14 | }
15 | ```
16 |
17 | Now it is clear that the dimensions should be `Float` values. It is also clear that we are not using the convention of specifying the top-left and bottom-right corners. It could be clearer about whether the `x` and `y` is the point in the top-left or in the middle though!
18 |
19 | Anyway, using records like this also gives you access to syntax like `rect.x`, `.x`, and `{ rect | x = 40 }`. It is not clear how to design features like that for arbitrarily sized tuples, so we did not. We already have a way, and it is more self-documenting!
20 |
--------------------------------------------------------------------------------
/hints/type-annotations.md:
--------------------------------------------------------------------------------
1 |
2 | # Hints for Type Annotation Problems
3 |
4 | At the root of this kind of issue is always the fact that a type annotation in your code does not match the corresponding definition. Now that may mean that the type annotation is "wrong" or it may mean that the definition is "wrong". The compiler cannot figure out your intent, only that there is some mismatch.
5 |
6 | This document is going to outline the various things that can go wrong and show some examples.
7 |
8 |
9 | ## Annotation vs. Definition
10 |
11 | The most common issue is with user-defined type variables that are too general. So let's say you have defined a function like this:
12 |
13 | ```elm
14 | addPair : (a, a) -> a
15 | addPair (x, y) =
16 | x + y
17 | ```
18 |
19 | The issue is that the type annotation is saying "I will accept a tuple containing literally *anything*" but the definition is using `(+)` which requires things to be numbers. So the compiler is going to infer that the true type of the definition is this:
20 |
21 | ```elm
22 | addPair : (number, number) -> number
23 | ```
24 |
25 | So you will probably see an error saying "I cannot match `a` with `number`" which is essentially saying, you are trying to provide a type annotation that is **too general**. You are saying `addPair` accepts anything, but in fact, it can only handle numbers.
26 |
27 | In cases like this, you want to go with whatever the compiler inferred. It is good at figuring this kind of stuff out ;)
28 |
29 |
30 | ## Annotation vs. Itself
31 |
32 | It is also possible to have a type annotation that clashes with itself. This is probably more rare, but someone will run into it eventually. Let's use another version of `addPair` with problems:
33 |
34 | ```elm
35 | addPair : (Int, Int) -> number
36 | addPair (x, y) =
37 | x + y
38 | ```
39 |
40 | In this case the annotation says we should get a `number` out, but because we were specific about the inputs being `Int`, the output should also be an `Int`.
41 |
42 |
43 | ## Annotation vs. Internal Annotation
44 |
45 | A quite tricky case is when an outer type annotation clashes with an inner type annotation. Here is an example of this:
46 |
47 | ```elm
48 | filter : (a -> Bool) -> List a -> List a
49 | filter isOkay list =
50 | let
51 | keepIfOkay : a -> Maybe a
52 | keepIfOkay x =
53 | if isOkay x then Just x else Nothing
54 | in
55 | List.filterMap keepIfOkay list
56 | ```
57 |
58 | This case is very unfortunate because all the type annotations are correct, but there is a detail of how type inference works right now that **user-defined type variables are not shared between annotations**. This can lead to probably the worst type error messages we have because the problem here is that `a` in the outer annotation does not equal `a` in the inner annotation.
59 |
60 | For now the best route is to leave off the inner annotation. It is unfortunate, and hopefully we will be able to do a nicer thing in future releases.
61 |
--------------------------------------------------------------------------------
/installers/README.md:
--------------------------------------------------------------------------------
1 | # Installing Elm
2 |
3 | The normal path is to work through [the guide](https://guide.elm-lang.org/) until you need to install, but you can skip to installation directly by going [here](https://guide.elm-lang.org/install/terminal.html).
4 |
5 |
6 |
7 |
8 | ## Installing Multiple Versions
9 |
10 | The secret is that Elm is just a single executable file. If you are developing a project in `~/Desktop/project/` you can download this file into that directory and run commands like `~/Desktop/project/elm make src/Main.elm` or `./elm make src/Main.elm`. You just run the local copy of the executable file!
11 |
12 | The instructions for [Mac][mac] and [Linux][lin] explain how to do this in more detail. You can follow the same steps on Windows, but you need to do each step by hand. (E.g. download the file through your browser rather than with a terminal command.)
13 |
14 | [mac]: https://github.com/elm/compiler/blob/master/installers/mac/README.md
15 | [lin]: https://github.com/elm/compiler/blob/master/installers/linux/README.md
16 |
17 |
18 |
19 | ## Installing Previous Versions
20 |
21 | The past binaries for Mac, Linux, and Windows are hosted [here](https://github.com/elm/compiler/releases).
22 |
23 | You can download the executable files directly and use them locally.
24 |
25 |
26 |
27 |
28 | ## Uninstall
29 |
30 | - [Mac](https://github.com/elm/compiler/blob/master/installers/mac/README.md#uninstall)
31 | - [Linux](https://github.com/elm/compiler/blob/master/installers/linux/README.md#uninstall)
32 | - [Windows](https://github.com/elm/compiler/blob/master/installers/win/README.md#uninstall)
33 |
--------------------------------------------------------------------------------
/installers/linux/Dockerfile:
--------------------------------------------------------------------------------
1 | # Based initially on https://gist.github.com/rlefevre/1523f47e75310e28eee243c9c5651ac9
2 | #
3 | # Build Linux x64 binary from elm compiler top-level directory:
4 | # $ docker build -t elm -f installers/linux/Dockerfile .
5 | #
6 | # Retrieve elm Linux binary:
7 | # $ docker cp $(docker create elm):/usr/local/bin/elm DESTINATION_DIRECTORY
8 | #
9 | # Delete docker elm image:
10 | # $ docker rmi elm
11 | #
12 | # Display all images:
13 | # $ docker images -a
14 | #
15 | # Delete all unused docker images:
16 | # $ docker system prune -a
17 |
18 | # Use Alpine 3.11 with GHC 8.6.5
19 | FROM alpine:3.11
20 |
21 | # Install packages required to build elm
22 | RUN apk add --no-cache ghc cabal wget musl-dev zlib-dev zlib-static ncurses-dev ncurses-static
23 |
24 | WORKDIR /elm
25 |
26 | # Import source code
27 | COPY builder builder
28 | COPY compiler compiler
29 | COPY reactor reactor
30 | COPY terminal terminal
31 | COPY cabal.config elm.cabal LICENSE ./
32 |
33 | # Build statically linked elm binary
34 | RUN cabal new-update
35 | RUN cabal new-build --ghc-option=-optl=-static --ghc-option=-split-sections
36 | RUN cp ./dist-newstyle/build/x86_64-linux/ghc-*/elm-*/x/elm/build/elm/elm /usr/local/bin/elm
37 |
38 | # Remove debug symbols to optimize the binary size
39 | RUN strip -s /usr/local/bin/elm
40 |
--------------------------------------------------------------------------------
/installers/linux/README.md:
--------------------------------------------------------------------------------
1 | # Install Instructions
2 |
3 | The pre-compiled binary for Linux works on a very wide range of distributions.
4 |
5 | It should be possible to install it by running the following commands in your terminal:
6 |
7 | ```bash
8 | # Move to your Desktop so you can see what is going on easier.
9 | #
10 | cd ~/Desktop/
11 |
12 | # Download the 0.19.1 binary for Linux.
13 | #
14 | # +-----------+----------------------+
15 | # | FLAG | MEANING |
16 | # +-----------+----------------------+
17 | # | -L | follow redirects |
18 | # | -o elm.gz | name the file elm.gz |
19 | # +-----------+----------------------+
20 | #
21 | curl -L -o elm.gz https://github.com/elm/compiler/releases/download/0.19.1/binary-for-linux-64-bit.gz
22 |
23 | # There should now be a file named `elm.gz` on your Desktop.
24 | #
25 | # The downloaded file is compressed to make it faster to download.
26 | # This next command decompresses it, replacing `elm.gz` with `elm`.
27 | #
28 | gunzip elm.gz
29 |
30 | # There should now be a file named `elm` on your Desktop!
31 | #
32 | # Every file has "permissions" about whether it can be read, written, or executed.
33 | # So before we use this file, we need to mark this file as executable:
34 | #
35 | chmod +x elm
36 |
37 | # The `elm` file is now executable. That means running `~/Desktop/elm --help`
38 | # should work. Saying `./elm --help` works the same.
39 | #
40 | # But we want to be able to say `elm --help` without specifying the full file
41 | # path every time. We can do this by moving the `elm` binary to one of the
42 | # directories listed in your `PATH` environment variable:
43 | #
44 | sudo mv elm /usr/local/bin/
45 |
46 | # Now it should be possible to run the `elm` binary just by saying its name!
47 | #
48 | elm --help
49 | ```
50 |
51 |
52 |
53 | ## Wait, what is the `PATH` variable?
54 |
55 | When you run a command like `elm make src/Main.elm`, your computer starts by trying to find an executable file called `elm`.
56 |
57 | The `PATH` is the list of directories that get searched. You can see these directories by running:
58 |
59 | ```bash
60 | echo $PATH
61 | ```
62 |
63 | This prints `/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin` on my computer. The directories are separated by a `:` so there are five possibilities listed here.
64 |
65 | When I run `elm make src/Main.elm`, my terminal starts by searching these five directories for an executable file named `elm`. It finds `/usr/local/bin/elm` and then runs `/usr/local/bin/elm make src/Main.elm` with whatever arguments I gave.
66 |
67 | So the `PATH` environment variable is a convention that allows you to refer to a specific executable file without knowing exactly where it lives on your computer. This is actually how all "terminal commands" work! Commands like `ls` are really executable files that live in directories listed in your `PATH` variable.
68 |
69 | So the point of running `sudo mv elm /usr/local/bin/` is to turn the `elm` binary into a terminal command, allowing us to call it just like `ls` and `cd`.
70 |
71 | **Note:** Why do we need to use `sudo` for that one command? Imagine if some program was able to add executables named `ls` or `cd` to `/usr/local/bin` that did something tricky and unexpected. That would be a security problem! Many distributions make this scenario less likely by requiring special permissions to modify the `/usr/local/bin/` directory.
72 |
73 |
74 |
75 |
76 | ## Uninstall
77 |
78 | The following commands should remove everything:
79 |
80 | ```bash
81 | # Remove the `elm` executable.
82 | #
83 | sudo rm /usr/local/bin/elm
84 |
85 | # Remove any cached files. The files here reduce compile times when
86 | # starting new projects and make it possible to work offline in more
87 | # cases. No need to keep it around if you are uninstalling though!
88 | #
89 | rm -r ~/.elm/
90 | ```
91 |
92 | If you have any Elm projects still on your computer, you can remove their `elm-stuff/` directories as well.
93 |
94 |
--------------------------------------------------------------------------------
/installers/mac/Distribution.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Elm
4 |
5 |
6 |
7 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | binaries.pkg
33 |
34 |
--------------------------------------------------------------------------------
/installers/mac/README.md:
--------------------------------------------------------------------------------
1 | # Install Instructions
2 |
3 | The easiest way to install is to to use [the Mac installer](https://github.com/elm/compiler/releases/download/0.19.1/installer-for-mac.pkg)!
4 |
5 | But it is also possible to install by running the following commands in your terminal:
6 |
7 | ```bash
8 | # Move to your Desktop so you can see what is going on easier.
9 | #
10 | cd ~/Desktop/
11 |
12 | # Download the 0.19.1 binary for Linux.
13 | #
14 | # +-----------+----------------------+
15 | # | FLAG | MEANING |
16 | # +-----------+----------------------+
17 | # | -L | follow redirects |
18 | # | -o elm.gz | name the file elm.gz |
19 | # +-----------+----------------------+
20 | #
21 | curl -L -o elm.gz https://github.com/elm/compiler/releases/download/0.19.1/binary-for-mac-64-bit.gz
22 |
23 | # There should now be a file named `elm.gz` on your Desktop.
24 | #
25 | # The downloaded file is compressed to make it faster to download.
26 | # This next command decompresses it, replacing `elm.gz` with `elm`.
27 | #
28 | gunzip elm.gz
29 |
30 | # There should now be a file named `elm` on your Desktop!
31 | #
32 | # Every file has "permissions" about whether it can be read, written, or executed.
33 | # So before we use this file, we need to mark this file as executable:
34 | #
35 | chmod +x elm
36 |
37 | # The `elm` file is now executable. That means running `~/Desktop/elm --help`
38 | # should work. Saying `./elm --help` works the same.
39 | #
40 | # But we want to be able to say `elm --help` without specifying the full file
41 | # path every time. We can do this by moving the `elm` binary to one of the
42 | # directories listed in your `PATH` environment variable:
43 | #
44 | sudo mv elm /usr/local/bin/
45 |
46 | # Now it should be possible to run the `elm` binary just by saying its name!
47 | #
48 | elm --help
49 | ```
50 |
51 |
52 |
53 | ## What is the `PATH` variable?
54 |
55 | When you run a command like `elm make src/Main.elm`, your computer starts by trying to find an executable file called `elm`.
56 |
57 | The `PATH` is the list of directories that get searched. You can see these directories by running:
58 |
59 | ```bash
60 | echo $PATH
61 | ```
62 |
63 | This prints `/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin` on my computer. The directories are separated by a `:` so there are five possibilities listed here.
64 |
65 | When I run `elm make src/Main.elm`, my terminal starts by searching these five directories for an executable file named `elm`. It finds `/usr/local/bin/elm` and then runs `/usr/local/bin/elm make src/Main.elm` with whatever arguments I gave.
66 |
67 | So the `PATH` environment variable is a convention that allows you to refer to a specific executable file without knowing exactly where it lives on your computer. This is actually how all "terminal commands" work! Commands like `ls` are really executable files that live in directories listed in your `PATH` variable.
68 |
69 | So the point of running `sudo mv elm /usr/local/bin/` is to turn the `elm` binary into a terminal command, allowing us to call it just like `ls` and `cd`.
70 |
71 | **Note:** Why do we need to use `sudo` for that one command? Imagine if some program was able to add executables named `ls` or `cd` to `/usr/local/bin` that did something tricky and unexpected. That would be a security problem! Many distributions make this scenario less likely by requiring special permissions to modify the `/usr/local/bin/` directory.
72 |
73 |
74 |
75 |
76 | ## Uninstall
77 |
78 | The following commands should remove everything:
79 |
80 | ```bash
81 | # Remove the `elm` executable.
82 | #
83 | sudo rm /usr/local/bin/elm
84 |
85 | # Remove any cached files. The files here reduce compile times when
86 | # starting new projects and make it possible to work offline in more
87 | # cases. No need to keep it around if you are uninstalling though!
88 | #
89 | rm -r ~/.elm/
90 | ```
91 |
92 | If you have any Elm projects still on your computer, you can remove their `elm-stuff/` directories as well.
93 |
94 |
--------------------------------------------------------------------------------
/installers/mac/Resources/en.lproj/conclusion.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf2509
2 | \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fmodern\fcharset0 CourierNewPSMT;}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\expandedcolortbl;;}
5 | \paperw11900\paperh16840\margl1440\margr1440\vieww11180\viewh8400\viewkind0
6 | \pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
7 |
8 | \f0\fs28 \cf0 Try opening the terminal and running commands like:\
9 | \
10 |
11 | \f1 elm init\
12 | elm make src/Main.elm --optimize\
13 | elm repl
14 | \f0 \
15 | \
16 | Check out {\field{\*\fldinst{HYPERLINK "https://guide.elm-lang.org/"}}{\fldrslt this tutorial}} for more advice!}
--------------------------------------------------------------------------------
/installers/mac/Resources/en.lproj/welcome.rtf:
--------------------------------------------------------------------------------
1 | {\rtf1\ansi\ansicpg1252\cocoartf2509
2 | \cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fmodern\fcharset0 CourierNewPSMT;}
3 | {\colortbl;\red255\green255\blue255;}
4 | {\*\expandedcolortbl;;}
5 | \paperw11900\paperh16840\margl1440\margr1440\vieww10800\viewh8400\viewkind0
6 | \pard\tx566\tx1133\tx1700\tx2267\tx2834\tx3401\tx3968\tx4535\tx5102\tx5669\tx6236\tx6803\pardirnatural\partightenfactor0
7 |
8 | \f0\fs28 \cf0 Thank you for trying out Elm!\
9 | \
10 | This installer makes
11 | \f1 elm
12 | \f0 available in your terminal.}
--------------------------------------------------------------------------------
/installers/mac/helper-scripts/elm-startup.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | open 'http://guide.elm-lang.org'
4 |
--------------------------------------------------------------------------------
/installers/mac/helper-scripts/uninstall.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | echo "Warning: You are about to remove all Elm executables!"
6 |
7 | installdir=/usr/local/bin
8 |
9 | for bin in elm elm-compiler elm-get elm-reactor elm-repl elm-doc elm-server elm-package elm-make
10 | do
11 | if [ -f $installdir/$bin ]; then
12 | sudo rm -f $installdir/$bin
13 | fi
14 | if [ -f $installdir/$bin-unwrapped ]; then
15 | sudo rm -f $installdir/$bin-unwrapped
16 | fi
17 |
18 | done
19 |
20 | sharedir=/usr/local/share/elm
21 | sudo rm -rf $sharedir
22 |
--------------------------------------------------------------------------------
/installers/mac/make-installer.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | # Run the following command to create an installer:
3 | #
4 | # bash make-installer.sh
5 | #
6 |
7 |
8 |
9 | #### SETUP ####
10 |
11 | set -e
12 |
13 | # Create directory structure for new pkgs
14 | pkg_root=$(mktemp -d -t package-artifacts)
15 | pkg_binaries=$pkg_root
16 | pkg_scripts=$pkg_root/Scripts
17 |
18 | mkdir -p $pkg_binaries
19 | mkdir -p $pkg_scripts
20 |
21 | usr_binaries=/usr/local/bin
22 |
23 |
24 | #### BUILD ASSETS ####
25 |
26 | cp ../../dist/build/elm/elm $pkg_binaries/elm
27 |
28 | cp $(pwd)/preinstall $pkg_scripts
29 | cp $(pwd)/postinstall $pkg_scripts
30 |
31 | pkgbuild \
32 | --sign "Developer ID Installer: " \
33 | --identifier org.elm-lang.binary \
34 | --install-location $usr_binaries \
35 | --scripts $pkg_scripts \
36 | --filter 'Scripts.*' \
37 | --root $pkg_root \
38 | binaries.pkg
39 |
40 |
41 | #### BUNDLE ASSETS ####
42 |
43 | rm -f installer-for-mac.pkg
44 |
45 | productbuild \
46 | --sign "Developer ID Installer: " \
47 | --identifier org.elm-lang.installer \
48 | --distribution Distribution.xml \
49 | --package-path . \
50 | --resources Resources \
51 | installer-for-mac.pkg
52 |
53 |
54 | #### CLEAN UP ####
55 |
56 | rm binaries.pkg
57 | rm -rf $pkg_root
58 |
59 |
60 | #### BEGIN NOTARIZATION ####
61 |
62 | xcrun altool \
63 | --notarize-app \
64 | --primary-bundle-id "org.elm-lang.installer" \
65 | --username "" \
66 | --password "@keychain:Developer-altool" \
67 | --file "installer-for-mac.pkg"
68 |
69 | # From https://scriptingosx.com/2019/09/notarize-a-command-line-tool/
70 | #
71 | #### Check on notarization:
72 | #
73 | # xcrun altool \
74 | # --notarization-info "" \
75 | # --username "" \
76 | # --password "@keychain:Developer-altool"
77 | #
78 | #
79 | #### Staple Notarization:
80 | #
81 | # xcrun stapler staple installer-for-mac.pkg
82 |
--------------------------------------------------------------------------------
/installers/mac/postinstall:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -ex
4 |
5 | echo "$(date)" > /tmp/elm-installer.log
6 |
--------------------------------------------------------------------------------
/installers/mac/preinstall:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | set -e
4 |
5 | installdir=/usr/local/bin
6 |
7 | for bin in elm elm-compiler elm-package elm-reactor elm-repl
8 | do
9 | if [ -f $installdir/$bin ]; then
10 | sudo rm -f $installdir/$bin
11 | fi
12 | if [ -f $installdir/$bin-unwrapped ]; then
13 | sudo rm -f $installdir/$bin-unwrapped
14 | fi
15 | done
16 |
17 | sharedir=/usr/local/share/elm
18 | sudo rm -rf $sharedir
19 |
--------------------------------------------------------------------------------
/installers/npm/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | packages/*/elm
3 | packages/*/elm.exe
4 |
--------------------------------------------------------------------------------
/installers/npm/.npmignore:
--------------------------------------------------------------------------------
1 | README.md
2 | .gitignore
3 | .git
4 |
--------------------------------------------------------------------------------
/installers/npm/README.md:
--------------------------------------------------------------------------------
1 | # Elm Installer
2 |
3 | [Elm](https://elm-lang.org) is a functional programming language that compiles to JavaScript.
4 |
5 | Head over to [The Official Guide](https://guide.elm-lang.org/) to start learning Elm!
6 |
7 |
8 |
9 |
10 | ## What is this package for?
11 |
12 | For normal installs, I reccomend using the instructions [here](https://guide.elm-lang.org/install/elm.html) instead. This package is only for people who enjoy using `npm` even when it is not necessary, or for people who want to use `npm` for certain scenarios such as:
13 |
14 | **Multiple versions**
15 |
16 | People using Elm at work may use different versions of Elm in different projects. They can run `npm install elm@latest-0.19.1` in each project and use the binary at `./node_modules/.bin/elm` for compilation.
17 |
18 | **Continuous integration**
19 |
20 | The `npm` installer works for this, but there are faster and more reliable options:
21 |
22 | 1. You can download `elm` directly from GitHub with [this script](https://github.com/elm/compiler/blob/master/installers/linux/README.md). This allows you to skip `npm` entirely.
23 | 2. Many continuous integration have ways to cache files ([example](https://docs.travis-ci.com/user/caching/)) to make builds faster and more reliable. This is the ideal setup.
24 |
25 | That said, it works to use the `npm` installer on CI if you prefer that option.
26 |
27 |
28 |
29 |
30 | ## Install Locally
31 |
32 | The following command should download the latest Elm 0.19.1 binary:
33 |
34 | ```
35 | npm install elm@latest-0.19.1
36 | ```
37 |
38 | You should be able to run `./node_modules/bin/elm --version` within your project and see `0.19.1`. Now you can compile with `./node_modules/bin/elm make src/Main.elm` and not disrupt other packages.
39 |
40 | Use `npm install elm@latest-0.19.0` or `npm install elm@latest-0.18.0` for earlier versions.
41 |
42 | **Note:** The `latest-X.Y.Z` convention is used in case we need to publish patches for the `npm` installer within a given Elm release. For example, say `npm` decides that some transitive dependency is not secure. Nothing is changing about Elm or the binaries, but we need to publish a new `npm` installer that fixes this issue.
43 |
44 |
--------------------------------------------------------------------------------
/installers/npm/bin/elm:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 |
3 | var child_process = require('child_process');
4 |
5 |
6 | // Some npm users enable --ignore-scripts (a good security measure) so
7 | // they do not run the post-install hook and install.js does not run.
8 | // Instead they will run this script.
9 | //
10 | // On Mac and Linux, we hard link the elm executable into the exact same
11 | // location as this file. Since npm uses symlinks on these platforms,
12 | // that means that the first run will invoke this file and subsequent
13 | // runs will call the elm binary directly.
14 | //
15 | // On Windows, our binary file must be named elm.exe for it to run properly.
16 | // Instead of symlinks, npm creates two files:
17 | //
18 | // - node_modules/.bin/elm (a bash file)
19 | // - node_modules/.bin/elm.cmd (a batch file)
20 | //
21 | // Both files specifically invoke `node` to run the file listed at package.bin,
22 | // so there is no way around instantiating node for no reason on Windows.
23 |
24 |
25 | var binaryPath = require('../binary.js')();
26 | child_process
27 | .spawn(binaryPath, process.argv.slice(2), { stdio: 'inherit' })
28 | .on('exit', process.exit);
29 |
--------------------------------------------------------------------------------
/installers/npm/binary.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs');
2 | var package = require('./package.json');
3 | var path = require('path');
4 |
5 |
6 |
7 | // MAIN
8 | //
9 | // This function is used by install.js and by the bin/elm backup that gets
10 | // called when --ignore-scripts is enabled.
11 |
12 |
13 | module.exports = function()
14 | {
15 | // figure out package of binary
16 | var version = package.version.replace(/^(\d+\.\d+\.\d+).*$/, '$1'); // turn '1.2.3-alpha' into '1.2.3'
17 | var subPackageName = '@elm_binaries/' + process.platform + '_' + process.arch;
18 |
19 | verifyPlatform(version, subPackageName);
20 |
21 | var fileName = process.platform === 'win32' ? 'elm.exe' : 'elm';
22 |
23 | try
24 | {
25 | var subBinaryPath = require.resolve(subPackageName + '/' + fileName);
26 | }
27 | catch (error)
28 | {
29 | if (error && error.code === 'MODULE_NOT_FOUND')
30 | {
31 | exitFailure(version, missingSubPackageHelp(subPackageName));
32 | }
33 | else
34 | {
35 | exitFailure(version, 'I had trouble requiring the binary package for your platform (' + subPackageName + '):\n\n' + error);
36 | }
37 | }
38 |
39 | // Yarn 2 and later ("Berry") always invokes `node` (regardless of configuration)
40 | // so we cannot do any optimizations there
41 | var isYarnBerry = /\byarn\/(?!1\.)/.test(process.env.npm_config_user_agent || "");
42 |
43 | // as mentioned in bin/elm we cannot do any optimizations on Windows
44 | if (process.platform === 'win32' || isYarnBerry)
45 | {
46 | return subBinaryPath;
47 | }
48 |
49 | // figure out where to put the binary
50 | var binaryPath = path.resolve(__dirname, package.bin.elm);
51 | var tmpPath = binaryPath + '.tmp';
52 |
53 | // optimize by replacing the JS bin/elm with the native binary directly
54 | try
55 | {
56 | // atomically replace the file with a hard link to the binary
57 | fs.linkSync(subBinaryPath, tmpPath);
58 | fs.renameSync(tmpPath, binaryPath);
59 | }
60 | catch (error)
61 | {
62 | exitFailure(version, 'I had some trouble writing file to disk. It is saying:\n\n' + error);
63 | }
64 |
65 | return binaryPath;
66 | }
67 |
68 |
69 |
70 | // VERIFY PLATFORM
71 |
72 |
73 | function verifyPlatform(version, subPackageName)
74 | {
75 | if (subPackageName in package.optionalDependencies) return;
76 |
77 | var situation = process.platform + '_' + process.arch;
78 | console.error(
79 | '-- ERROR -----------------------------------------------------------------------\n\n'
80 | + 'I am detecting that your computer (' + situation + ') may not be compatible with any\n'
81 | + 'of the official pre-built binaries.\n\n'
82 | + 'I recommend against using the npm installer for your situation. Check out the\n'
83 | + 'alternative installers at https://github.com/elm/compiler/releases/tag/' + version + '\n'
84 | + 'to see if there is something that will work better for you.\n\n'
85 | + 'From there I recommend asking for guidance on Slack or Discourse to find someone\n'
86 | + 'who can help with your specific situation.\n\n'
87 | + '--------------------------------------------------------------------------------\n'
88 | );
89 | process.exit(1);
90 | }
91 |
92 |
93 |
94 | // EXIT FAILURE
95 |
96 |
97 | function exitFailure(version, message)
98 | {
99 | console.error(
100 | '-- ERROR -----------------------------------------------------------------------\n\n'
101 | + message
102 | + '\n\nNOTE: You can avoid npm entirely by downloading directly from:\n'
103 | + 'https://github.com/elm/compiler/releases/tag/' + version + '\n'
104 | + 'All this package does is distribute a file from there.\n\n'
105 | + '--------------------------------------------------------------------------------\n'
106 | );
107 | process.exit(1);
108 | }
109 |
110 |
111 |
112 | // MISSING SUB PACKAGE HELP
113 |
114 |
115 | function missingSubPackageHelp(subPackageName)
116 | {
117 | return (
118 | 'I tried to get `elm` from ' + subPackageName + ', but something went wrong.\n'
119 | + 'This can happen if you use the "--omit=optional" or "--no-optional" npm flag, or\n'
120 | + 'if your "node_modules" folder was copied over from a different computer (or VM).\n'
121 | );
122 | }
123 |
--------------------------------------------------------------------------------
/installers/npm/install.js:
--------------------------------------------------------------------------------
1 | require('./binary.js')();
2 |
--------------------------------------------------------------------------------
/installers/npm/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "elm",
3 | "version": "0.19.1-6",
4 | "description": "Installer for Elm: just downloads the binary into node_modules",
5 | "license": "BSD-3-Clause",
6 | "repository": {
7 | "type": "git",
8 | "url": "https://github.com/elm/compiler.git"
9 | },
10 | "homepage": "https://github.com/elm/compiler/tree/master/installers/npm",
11 | "bugs": "https://github.com/elm/compiler/issues",
12 | "author": {
13 | "name": "Evan Czaplicki",
14 | "email": "evan@elm-lang.org",
15 | "url": "https://github.com/evancz"
16 | },
17 | "engines": {
18 | "node": ">=7.0.0"
19 | },
20 | "scripts": {
21 | "install": "node install.js"
22 | },
23 | "files": [
24 | "install.js",
25 | "binary.js",
26 | "bin",
27 | "bin/elm"
28 | ],
29 | "keywords": [
30 | "bin",
31 | "binary",
32 | "binaries",
33 | "elm",
34 | "install",
35 | "installer"
36 | ],
37 | "bin": { "elm": "bin/elm" },
38 | "optionalDependencies": {
39 | "@elm_binaries/darwin_arm64": "0.19.1-0",
40 | "@elm_binaries/darwin_x64": "0.19.1-0",
41 | "@elm_binaries/linux_x64": "0.19.1-0",
42 | "@elm_binaries/win32_x64": "0.19.1-0"
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/installers/npm/packages/darwin_arm64/README.md:
--------------------------------------------------------------------------------
1 | # Elm Binary for macOS (arm64)
2 |
3 | Some people install [Elm](https://elm-lang.org/) with `npm`. This package helps make [`npm install elm`](https://www.npmjs.com/package/elm) a bit faster and a bit more reliable. It is not intended for direct use!
4 |
5 | If you do not need to use `npm`, the official binaries are published via [GitHub releases](https://github.com/elm/compiler/releases) with installation instructions.
6 |
--------------------------------------------------------------------------------
/installers/npm/packages/darwin_arm64/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@elm_binaries/darwin_arm64",
3 | "version": "0.19.1-0",
4 | "description": "Download the Elm binary for macOS (arm64)",
5 | "repository": "https://github.com/elm/compiler",
6 | "license": "BSD-3-Clause",
7 | "os": [ "darwin" ],
8 | "cpu": [ "arm64" ]
9 | }
10 |
--------------------------------------------------------------------------------
/installers/npm/packages/darwin_x64/README.md:
--------------------------------------------------------------------------------
1 | # Elm Binary for macOS (x64)
2 |
3 | Some people install [Elm](https://elm-lang.org/) with `npm`. This package helps make [`npm install elm`](https://www.npmjs.com/package/elm) a bit faster and a bit more reliable. It is not intended for direct use!
4 |
5 | If you do not need to use `npm`, the official binaries are published via [GitHub releases](https://github.com/elm/compiler/releases) with installation instructions.
6 |
--------------------------------------------------------------------------------
/installers/npm/packages/darwin_x64/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@elm_binaries/darwin_x64",
3 | "version": "0.19.1-0",
4 | "description": "Download the Elm binary for macOS (x64)",
5 | "repository": "https://github.com/elm/compiler",
6 | "license": "BSD-3-Clause",
7 | "os": [ "darwin" ],
8 | "cpu": [ "x64" ]
9 | }
10 |
--------------------------------------------------------------------------------
/installers/npm/packages/linux_arm64/README.md:
--------------------------------------------------------------------------------
1 | # Elm Binary for Linux (arm64)
2 |
3 | Some people install [Elm](https://elm-lang.org/) with `npm`. This package helps make [`npm install elm`](https://www.npmjs.com/package/elm) a bit faster and a bit more reliable. It is not intended for direct use!
4 |
5 | If you do not need to use `npm`, the official binaries are published via [GitHub releases](https://github.com/elm/compiler/releases) with installation instructions.
6 |
--------------------------------------------------------------------------------
/installers/npm/packages/linux_arm64/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@elm_binaries/linux_arm64",
3 | "version": "0.19.1-0",
4 | "description": "Download the Elm binary for Linux (arm64)",
5 | "repository": "https://github.com/elm/compiler",
6 | "license": "BSD-3-Clause",
7 | "os": [ "linux" ],
8 | "cpu": [ "arm64" ]
9 | }
10 |
--------------------------------------------------------------------------------
/installers/npm/packages/linux_x64/README.md:
--------------------------------------------------------------------------------
1 | # Elm Binary for Linux (x64)
2 |
3 | Some people install [Elm](https://elm-lang.org/) with `npm`. This package helps make [`npm install elm`](https://www.npmjs.com/package/elm) a bit faster and a bit more reliable. It is not intended for direct use!
4 |
5 | If you do not need to use `npm`, the official binaries are published via [GitHub releases](https://github.com/elm/compiler/releases) with installation instructions.
6 |
--------------------------------------------------------------------------------
/installers/npm/packages/linux_x64/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@elm_binaries/linux_x64",
3 | "version": "0.19.1-0",
4 | "description": "Download the Elm binary for Linux (x64)",
5 | "repository": "https://github.com/elm/compiler",
6 | "license": "BSD-3-Clause",
7 | "os": [ "linux" ],
8 | "cpu": [ "x64" ]
9 | }
10 |
--------------------------------------------------------------------------------
/installers/npm/packages/win32_x64/README.md:
--------------------------------------------------------------------------------
1 | # Elm Binary for Windows (x64)
2 |
3 | Some people install [Elm](https://elm-lang.org/) with `npm`. This package helps make [`npm install elm`](https://www.npmjs.com/package/elm) a bit faster and a bit more reliable. It is not intended for direct use!
4 |
5 | If you do not need to use `npm`, the official binaries are published via [GitHub releases](https://github.com/elm/compiler/releases) with installation instructions.
6 |
--------------------------------------------------------------------------------
/installers/npm/packages/win32_x64/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "@elm_binaries/win32_x64",
3 | "version": "0.19.1-0",
4 | "description": "Download the Elm binary for Windows (x64)",
5 | "repository": "https://github.com/elm/compiler",
6 | "license": "BSD-3-Clause",
7 | "os": [ "win32" ],
8 | "cpu": [ "x64" ]
9 | }
10 |
--------------------------------------------------------------------------------
/installers/npm/troubleshooting.md:
--------------------------------------------------------------------------------
1 | # Troubleshooting
2 |
3 | I very highly recommend asking for help on [the Elm slack](https://elmlang.herokuapp.com).
4 |
5 | There are a lot of things that can go wrong when installing software, and it can really help to have a second pair of eyes on your situation!
6 |
7 | This document goes through a couple options that may help you out.
8 |
9 |
10 |
11 |
12 | ## Can you skip npm entirely?
13 |
14 | The most reliable way to get Elm installed using the official installers for Mac and Windows [here][download].
15 |
16 | You can also download the binaries directly. On Linux, you could do it in the terminal like this:
17 |
18 | ```bash
19 | cd ~/Desktop/
20 | curl -L -o elm.gz https://github.com/elm/compiler/releases/download/0.19.1/binary-for-linux-64-bit.gz
21 | gunzip elm.gz # unzip the file
22 | chmod +x elm # make the file executable
23 | sudo mv elm /usr/local/bin/ # put the executable in a directory likely to be listed in your PATH variable
24 | ```
25 |
26 | If these exact commands do not work for you, you can try to do the same thing by hand.
27 |
28 | Read the section below on `PATH` variables if you are not sure what that is!
29 |
30 | [download]: https://github.com/elm/compiler/releases/tag/0.19.1
31 |
32 |
33 |
34 |
35 | ## Do you need to use npm for some reason?
36 |
37 | The company running npm has a list of common troubleshooting situations [here](https://npm.community/c/support/troubleshooting), but it may be better to just try to find your specific case on Stack Overflow. Often there are permissions issues where you may need to use `sudo` with some command.
38 |
39 | ### Firewalls
40 |
41 | Some companies have a firewall.
42 |
43 | These companies usually have set the `HTTP_PROXY` or `HTTPS_PROXY` environment variable on your computer. This is more common with Windows computers.
44 |
45 | The result is that the requests for npm packages are being sent to a "proxy server" where they monitor traffic. Maybe they rule out certain domains, maybe they check data when it comes back from the actual URL, etc.
46 |
47 | It is probably best to ask someone about the situation on this, but you can test things out by temporarily using an alternate `HTTPS_PROXY` value with something like this:
48 |
49 | ```
50 | # Mac and Linux
51 | HTTPS_PROXY=http://proxy.example.com npm install -g elm
52 |
53 | # Windows
54 | set HTTPS_PROXY=http://proxy.example.com
55 | npm install -g elm
56 | ```
57 |
58 | Check out [this document](https://docs.npmjs.com/cli/using-npm/config/) for more information on how environment variables like [NO_PROXY](https://docs.npmjs.com/cli/using-npm/config#noproxy), [HTTP_PROXY](https://docs.npmjs.com/cli/using-npm/config#proxy), and [HTTPS_PROXY](https://docs.npmjs.com/cli/using-npm/config#https-proxy) are handled by npm.
59 |
60 |
61 |
62 |
63 | ## Do you know what a `PATH` variable is?
64 |
65 | When you run a command like `elm make src/Main.elm`, your computer starts by trying to find a file called `elm`.
66 |
67 | The `PATH` is a list of directories to search within. On Mac and Linux, you can see these directories by running:
68 |
69 | ```
70 | $ echo $PATH
71 | /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr/local/git/bin
72 | ```
73 |
74 | The are separated by `:` for some reason. So running `elm make src/Main.elm` starts by searching the `PATH` for files named `elm`. On my computer, it finds `/usr/local/bin/elm` and then can actually run the command.
75 |
76 | Is `elm` in one of the directories listed in your `PATH` variable? I recommend asking for help if you are in this scenario and unsure how to proceed.
77 |
--------------------------------------------------------------------------------
/installers/win/CreateInternetShortcut.nsh:
--------------------------------------------------------------------------------
1 | !macro CreateInternetShortcut FILENAME URL ICONFILE ICONINDEX
2 | WriteINIStr "${FILENAME}.url" "InternetShortcut" "URL" "${URL}"
3 | WriteINIStr "${FILENAME}.url" "InternetShortcut" "IconFile" "${ICONFILE}"
4 | WriteINIStr "${FILENAME}.url" "InternetShortcut" "IconIndex" "${ICONINDEX}"
5 | !macroend
--------------------------------------------------------------------------------
/installers/win/README.md:
--------------------------------------------------------------------------------
1 | # Installing on Windows
2 |
3 | The installer for Windows is available [here](https://guide.elm-lang.org/install.html).
4 |
5 |
6 |
7 |
8 | ## Uninstall
9 |
10 | First run the `C:\Program Files (x86)\Elm\0.19\uninstall.exe` file. This will remove Elm stuff from your `PATH`.
11 |
12 | Then remove the whole `C:\Users\\AppData\Roaming\elm` directory. Elm caches some packages and build artifacts to reduce compile times and to help you work offline. Getting rid of this directory will clear that information out!
13 |
14 |
15 |
16 | ## Building the Windows installer
17 |
18 | You will need the [NSIS installer](http://nsis.sourceforge.net/Download) to be installed.
19 |
20 | Once everything is installed, run something like this command:
21 |
22 | make_installer.cmd 0.19.0
23 |
24 | It will build an installer called `Elm-0.19.0-setup.exe`.
25 |
--------------------------------------------------------------------------------
/installers/win/inst.dat:
--------------------------------------------------------------------------------
1 | SetOutPath "$INSTDIR\bin"
2 | File "${FILES_SOURCE_PATH}\bin\elm.exe"
3 |
4 | SetOutPath "$INSTDIR"
5 | File "updatepath.vbs"
6 | File "removefrompath.vbs"
7 |
--------------------------------------------------------------------------------
/installers/win/logo.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm/compiler/2f6dd29258e880dbb7effd57a829a0470d8da48b/installers/win/logo.ico
--------------------------------------------------------------------------------
/installers/win/make_installer.cmd:
--------------------------------------------------------------------------------
1 |
2 | set version=%1
3 |
4 | mkdir files
5 | mkdir files\bin
6 |
7 | xcopy ..\..\dist\build\elm\elm.exe files\bin /s /e
8 | xcopy updatepath.vbs files
9 |
10 | if EXIST "%ProgramFiles%\NSIS" (
11 | set nsis="%ProgramFiles%\NSIS\makensis.exe"
12 | ) else (
13 | set nsis="%ProgramFiles(x86)%\NSIS\makensis.exe"
14 | )
15 |
16 | %nsis% /DPLATFORM_VERSION=%version% Nsisfile.nsi
17 |
18 | rd /s /q files
19 |
--------------------------------------------------------------------------------
/installers/win/removefrompath.vbs:
--------------------------------------------------------------------------------
1 | Set WshShell = CreateObject("WScript.Shell")
2 | ' Make sure there is no trailing slash at the end of elmBasePath
3 | elmBasePath = WScript.Arguments(0)
4 | 'const PathRegKey = "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path"
5 | const PathRegKey = "HKCU\Environment\Path"
6 |
7 | on error resume next
8 | path = WshShell.RegRead(PathRegKey)
9 | if err.number = 0 then
10 | Set regEx = New RegExp
11 | elmBasePath = Replace(Replace(Replace(elmBasePath, "\", "\\"), "(", "\("), ")", "\)")
12 | regEx.Pattern = elmBasePath & "\\\d+\.\d+(\.\d+|)\\bin(;|)"
13 | regEx.Global = True
14 | newPath = regEx.Replace(path, "")
15 | Call WshShell.RegWrite(PathRegKey, newPath, "REG_EXPAND_SZ")
16 | end if
17 | on error goto 0
18 |
--------------------------------------------------------------------------------
/installers/win/uninst.dat:
--------------------------------------------------------------------------------
1 | Delete "$INSTDIR\bin\elm.exe"
2 | RmDir "$INSTDIR\bin"
3 |
4 | Delete "$INSTDIR\updatepath.vbs"
5 | Delete "$INSTDIR\removefrompath.vbs"
6 | RmDir "$INSTDIR"
7 |
--------------------------------------------------------------------------------
/installers/win/updatepath.vbs:
--------------------------------------------------------------------------------
1 | Set WshShell = CreateObject("WScript.Shell")
2 | elmPath = WScript.Arguments(0)
3 | 'const PathRegKey = "HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment\Path"
4 | const PathRegKey = "HKCU\Environment\Path"
5 |
6 | on error resume next
7 | path = WshShell.RegRead(PathRegKey)
8 | if err.number <> 0 then
9 | path = ""
10 | end if
11 | on error goto 0
12 |
13 | newPath = elmPath & ";" & path
14 | Call WshShell.RegWrite(PathRegKey, newPath, "REG_EXPAND_SZ")
15 |
--------------------------------------------------------------------------------
/installers/win/welcome.bmp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm/compiler/2f6dd29258e880dbb7effd57a829a0470d8da48b/installers/win/welcome.bmp
--------------------------------------------------------------------------------
/reactor/assets/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm/compiler/2f6dd29258e880dbb7effd57a829a0470d8da48b/reactor/assets/favicon.ico
--------------------------------------------------------------------------------
/reactor/assets/source-code-pro.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm/compiler/2f6dd29258e880dbb7effd57a829a0470d8da48b/reactor/assets/source-code-pro.ttf
--------------------------------------------------------------------------------
/reactor/assets/source-sans-pro.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/elm/compiler/2f6dd29258e880dbb7effd57a829a0470d8da48b/reactor/assets/source-sans-pro.ttf
--------------------------------------------------------------------------------
/reactor/assets/styles.css:
--------------------------------------------------------------------------------
1 | @charset "UTF-8";
2 |
3 |
4 | /* FONTS */
5 |
6 | @font-face {
7 | font-family: 'Source Code Pro';
8 | font-style: normal;
9 | font-weight: 400;
10 | src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(/_elm/source-code-pro.ttf) format('truetype');
11 | }
12 |
13 | @font-face {
14 | font-family: 'Source Sans Pro';
15 | font-style: normal;
16 | font-weight: 400;
17 | src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url(/_elm/source-sans-pro.ttf) format('truetype');
18 | }
19 |
20 |
21 | /* GENERIC STUFF */
22 |
23 | html, head, body {
24 | margin: 0;
25 | height: 100%;
26 | }
27 |
28 | body {
29 | font-family: 'Source Sans Pro', 'Trebuchet MS', 'Lucida Grande', 'Bitstream Vera Sans', 'Helvetica Neue', sans-serif;
30 | color: #293c4b;
31 | }
32 |
33 | a {
34 | color: #60B5CC;
35 | text-decoration: none;
36 | }
37 |
38 | a:hover {
39 | text-decoration: underline;
40 | }
41 |
42 |
43 | /* INDEX */
44 |
45 | .header {
46 | width: 100%;
47 | background-color: #60B5CC;
48 | height: 8px;
49 | }
50 |
51 | .content {
52 | width: 960px;
53 | margin-left: auto;
54 | margin-right: auto;
55 | }
56 |
57 |
58 | /* COLUMNS */
59 |
60 | .left-column {
61 | float: left;
62 | width: 600px;
63 | padding-bottom: 80px;
64 | }
65 |
66 | .right-column {
67 | float: right;
68 | width: 300px;
69 | padding-bottom: 80px;
70 | }
71 |
72 |
73 | /* BOXES */
74 |
75 | .box {
76 | border: 1px solid #c7c7c7;
77 | border-radius: 5px;
78 | margin-bottom: 40px;
79 | }
80 |
81 | .box-header {
82 | display: block;
83 | overflow: hidden;
84 | padding: 7px 12px;
85 | background-color: #fafafa;
86 | text-align: center;
87 | border-radius: 5px;
88 | }
89 |
90 | .box-item {
91 | display: block;
92 | overflow: hidden;
93 | padding: 7px 12px;
94 | border-top: 1px solid #e1e1e1;
95 | }
96 |
97 | .box-footer {
98 | display: block;
99 | overflow: hidden;
100 | padding: 2px 12px;
101 | border-top: 1px solid #e1e1e1;
102 | text-align: center;
103 | background-color: #fafafa;
104 | height: 16px;
105 | }
106 |
107 |
108 | /* ICONS */
109 |
110 | .icon {
111 | display: inline-block;
112 | vertical-align: middle;
113 | padding-right: 0.5em;
114 | }
115 |
116 |
117 | /* PAGES */
118 |
119 | .page-name {
120 | float: left;
121 | }
122 |
123 | .page-size {
124 | float: right;
125 | color: #293c4b;
126 | }
127 |
128 | .page-size:hover {
129 | color: #60B5CC;
130 | }
131 |
132 |
133 | /* WAITING */
134 |
135 | .waiting {
136 | width: 100%;
137 | height: 100%;
138 | display: flex;
139 | flex-direction: column;
140 | justify-content: center;
141 | align-items: center;
142 | color: #9A9A9A;
143 | }
144 |
145 |
146 | /* NOT FOUND */
147 |
148 | .not-found {
149 | width: 100%;
150 | height: 100%;
151 | display: flex;
152 | flex-direction: column;
153 | justify-content: center;
154 | align-items: center;
155 | background-color: #F5F5F5;
156 | color: #9A9A9A;
157 | }
158 |
--------------------------------------------------------------------------------
/reactor/check.py:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env python
2 |
3 | import os
4 | import sys
5 |
6 |
7 | ## FIGURE OUT NEW MODIFICATION TIME
8 |
9 | def mostRecentModification(directory):
10 | mostRecent = 0
11 |
12 | for dirpath, dirs, files in os.walk(directory):
13 | for f in files:
14 | lastModified = os.path.getmtime(dirpath + '/' + f)
15 | mostRecent = max(int(lastModified), mostRecent)
16 |
17 | return mostRecent
18 |
19 |
20 | srcTime = mostRecentModification('ui/src')
21 | assetTime = mostRecentModification('ui/assets')
22 | mostRecent = max(srcTime, assetTime)
23 |
24 |
25 | ## FIGURE OUT OLD MODIFICATION TIME
26 |
27 | with open('ui/last-modified', 'a') as handle:
28 | pass
29 |
30 |
31 | prevMostRecent = 0
32 |
33 |
34 | with open('ui/last-modified', 'r+') as handle:
35 | line = handle.read()
36 | prevMostRecent = int(line) if line else 0
37 |
38 |
39 | ## TOUCH FILES IF NECESSARY
40 |
41 | if mostRecent > prevMostRecent:
42 | print "+------------------------------------------------------------+"
43 | print "| Some ui/ code changed. Touching src/Reactor/StaticFiles.hs |"
44 | print "| to trigger a recompilation of the Template Haskell stuff. |"
45 | print "+------------------------------------------------------------+"
46 | os.utime('src/Reactor/StaticFiles.hs', None)
47 | with open('ui/last-modified', 'w') as handle:
48 | handle.write(str(mostRecent))
49 |
--------------------------------------------------------------------------------
/reactor/elm.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "application",
3 | "source-directories": [
4 | "src"
5 | ],
6 | "elm-version": "0.19.1",
7 | "dependencies": {
8 | "direct": {
9 | "elm/browser": "1.0.1",
10 | "elm/core": "1.0.2",
11 | "elm/html": "1.0.0",
12 | "elm/http": "2.0.0",
13 | "elm/json": "1.1.2",
14 | "elm/project-metadata-utils": "1.0.0",
15 | "elm/svg": "1.0.1",
16 | "elm-explorations/markdown": "1.0.0"
17 | },
18 | "indirect": {
19 | "elm/bytes": "1.0.7",
20 | "elm/file": "1.0.1",
21 | "elm/parser": "1.1.0",
22 | "elm/time": "1.0.0",
23 | "elm/url": "1.0.0",
24 | "elm/virtual-dom": "1.0.2"
25 | }
26 | },
27 | "test-dependencies": {
28 | "direct": {},
29 | "indirect": {}
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/reactor/src/Index/Icon.elm:
--------------------------------------------------------------------------------
1 | module Index.Icon exposing
2 | ( home
3 | , image
4 | , file
5 | , gift
6 | , folder
7 | , package
8 | , plus
9 | , lookup
10 | )
11 |
12 | import Dict
13 | import Html exposing (Html)
14 | import Svg exposing (..)
15 | import Svg.Attributes exposing (class, width, height, viewBox, d, fill)
16 |
17 |
18 |
19 | -- ICON
20 |
21 |
22 | icon : String -> String -> String -> Html msg
23 | icon color size pathString =
24 | svg
25 | [ class "icon"
26 | , width size
27 | , height size
28 | , viewBox "0 0 1792 1792"
29 | ]
30 | [ path [ fill color, d pathString ] []
31 | ]
32 |
33 |
34 |
35 | -- NECESSARY ICONS
36 |
37 |
38 | home : Html msg
39 | home =
40 | icon "#babdb6" "36px" "M1472 992v480q0 26-19 45t-45 19h-384v-384h-256v384h-384q-26 0-45-19t-19-45v-480q0-1 .5-3t.5-3l575-474 575 474q1 2 1 6zm223-69l-62 74q-8 9-21 11h-3q-13 0-21-7l-692-577-692 577q-12 8-24 7-13-2-21-11l-62-74q-8-10-7-23.5t11-21.5l719-599q32-26 76-26t76 26l244 204v-195q0-14 9-23t23-9h192q14 0 23 9t9 23v408l219 182q10 8 11 21.5t-7 23.5z"
41 |
42 |
43 | image : Html msg
44 | image =
45 | icon "#babdb6" "16px" "M1596 380q28 28 48 76t20 88v1152q0 40-28 68t-68 28h-1344q-40 0-68-28t-28-68v-1600q0-40 28-68t68-28h896q40 0 88 20t76 48zm-444-244v376h376q-10-29-22-41l-313-313q-12-12-41-22zm384 1528v-1024h-416q-40 0-68-28t-28-68v-416h-768v1536h1280zm-128-448v320h-1024v-192l192-192 128 128 384-384zm-832-192q-80 0-136-56t-56-136 56-136 136-56 136 56 56 136-56 136-136 56z"
46 |
47 |
48 | file : Html msg
49 | file =
50 | icon "#babdb6" "16px" "M1596 380q28 28 48 76t20 88v1152q0 40-28 68t-68 28h-1344q-40 0-68-28t-28-68v-1600q0-40 28-68t68-28h896q40 0 88 20t76 48zm-444-244v376h376q-10-29-22-41l-313-313q-12-12-41-22zm384 1528v-1024h-416q-40 0-68-28t-28-68v-416h-768v1536h1280zm-1024-864q0-14 9-23t23-9h704q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-14 0-23-9t-9-23v-64zm736 224q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h704zm0 256q14 0 23 9t9 23v64q0 14-9 23t-23 9h-704q-14 0-23-9t-9-23v-64q0-14 9-23t23-9h704z"
51 |
52 |
53 | gift : Html msg
54 | gift =
55 | icon "#babdb6" "16px" "M1056 1356v-716h-320v716q0 25 18 38.5t46 13.5h192q28 0 46-13.5t18-38.5zm-456-844h195l-126-161q-26-31-69-31-40 0-68 28t-28 68 28 68 68 28zm688-96q0-40-28-68t-68-28q-43 0-69 31l-125 161h194q40 0 68-28t28-68zm376 256v320q0 14-9 23t-23 9h-96v416q0 40-28 68t-68 28h-1088q-40 0-68-28t-28-68v-416h-96q-14 0-23-9t-9-23v-320q0-14 9-23t23-9h440q-93 0-158.5-65.5t-65.5-158.5 65.5-158.5 158.5-65.5q107 0 168 77l128 165 128-165q61-77 168-77 93 0 158.5 65.5t65.5 158.5-65.5 158.5-158.5 65.5h440q14 0 23 9t9 23z"
56 |
57 |
58 | folder : Html msg
59 | folder =
60 | icon "#babdb6" "16px" "M1728 608v704q0 92-66 158t-158 66h-1216q-92 0-158-66t-66-158v-960q0-92 66-158t158-66h320q92 0 158 66t66 158v32h672q92 0 158 66t66 158z"
61 |
62 |
63 | package : Html msg
64 | package =
65 | icon "#babdb6" "16px" "M1088 832q0-26-19-45t-45-19h-256q-26 0-45 19t-19 45 19 45 45 19h256q26 0 45-19t19-45zm576-192v960q0 26-19 45t-45 19h-1408q-26 0-45-19t-19-45v-960q0-26 19-45t45-19h1408q26 0 45 19t19 45zm64-448v256q0 26-19 45t-45 19h-1536q-26 0-45-19t-19-45v-256q0-26 19-45t45-19h1536q26 0 45 19t19 45z"
66 |
67 |
68 | plus : Html msg
69 | plus =
70 | icon "#babdb6" "16px" "M1600 736v192q0 40-28 68t-68 28h-416v416q0 40-28 68t-68 28h-192q-40 0-68-28t-28-68v-416h-416q-40 0-68-28t-28-68v-192q0-40 28-68t68-28h416v-416q0-40 28-68t68-28h192q40 0 68 28t28 68v416h416q40 0 68 28t28 68z"
71 |
72 |
73 |
74 | -- LOOKUP
75 |
76 |
77 | lookup : String -> Html msg
78 | lookup fileName =
79 | let
80 | extension =
81 | getExtension fileName
82 | in
83 | Maybe.withDefault file (Dict.get extension extensionIcons)
84 |
85 |
86 | extensionIcons : Dict.Dict String (Html msg)
87 | extensionIcons =
88 | Dict.fromList
89 | [ ("jpg" , image)
90 | , ("jpeg", image)
91 | , ("png" , image)
92 | , ("gif" , image)
93 | ]
94 |
95 |
96 | getExtension : String -> String
97 | getExtension str =
98 | getExtensionHelp (String.split "." str)
99 |
100 |
101 | getExtensionHelp : List String -> String
102 | getExtensionHelp segments =
103 | case segments of
104 | [] ->
105 | ""
106 |
107 | [ext] ->
108 | String.toLower ext
109 |
110 | _ :: rest ->
111 | getExtensionHelp rest
112 |
--------------------------------------------------------------------------------
/reactor/src/Index/Navigator.elm:
--------------------------------------------------------------------------------
1 | module Index.Navigator exposing (view)
2 |
3 |
4 | import Html exposing (..)
5 | import Html.Attributes exposing (..)
6 | import Index.Icon as Icon
7 |
8 |
9 |
10 | -- VIEW
11 |
12 |
13 | view : String -> List String -> Html msg
14 | view root dirs =
15 | div
16 | [ style "font-size" "2em"
17 | , style "padding" "20px 0"
18 | , style "display" "flex"
19 | , style "align-items" "center"
20 | , style "height" "40px"
21 | ]
22 | (makeLinks root dirs "" [])
23 |
24 |
25 | makeLinks : String -> List String -> String -> List (Html msg) -> List (Html msg)
26 | makeLinks root dirs oldPath revAnchors =
27 | case dirs of
28 | dir :: otherDirs ->
29 | let
30 | newPath =
31 | oldPath ++ "/" ++ dir
32 |
33 | anchor =
34 | a [ href newPath ] [ text dir ]
35 | in
36 | makeLinks root otherDirs newPath (anchor :: revAnchors)
37 |
38 | [] ->
39 | let
40 | home =
41 | a [ href "/"
42 | , title root
43 | , style "display" "inherit"
44 | ]
45 | [ Icon.home
46 | ]
47 | in
48 | case revAnchors of
49 | [] ->
50 | [home]
51 |
52 | lastAnchor :: otherRevAnchors ->
53 | home :: slash :: List.foldl addSlash [lastAnchor] otherRevAnchors
54 |
55 |
56 | addSlash : Html msg -> List (Html msg) -> List (Html msg)
57 | addSlash front back =
58 | front :: slash :: back
59 |
60 |
61 | slash : Html msg
62 | slash =
63 | span [ style "padding" "0 8px" ] [ text "/" ]
64 |
--------------------------------------------------------------------------------
/reactor/src/Index/Skeleton.elm:
--------------------------------------------------------------------------------
1 | module Index.Skeleton exposing
2 | ( box
3 | , readmeBox
4 | )
5 |
6 | import Html exposing (..)
7 | import Html.Attributes exposing (..)
8 | import Markdown
9 |
10 | import Index.Icon as Icon
11 |
12 |
13 |
14 | -- VIEW BOXES
15 |
16 |
17 | type alias BoxArgs msg =
18 | { title : String
19 | , items : List (List (Html msg))
20 | , footer : Maybe (String, String)
21 | }
22 |
23 |
24 | box : BoxArgs msg -> Html msg
25 | box { title, items, footer } =
26 | let
27 | realItems =
28 | List.map (div [ class "box-item" ]) items
29 | in
30 | boxHelp title realItems footer
31 |
32 |
33 | readmeBox : String -> Html msg
34 | readmeBox markdown =
35 | let
36 | readme =
37 | Markdown.toHtml [ class "box-item" ] markdown
38 | in
39 | boxHelp "README" [readme] Nothing
40 |
41 |
42 | boxHelp : String -> List (Html msg) -> Maybe (String, String) -> Html msg
43 | boxHelp boxTitle items footer =
44 | div [ class "box" ] <|
45 | div [ class "box-header" ] [ text boxTitle ]
46 | :: items
47 | ++ [ boxFooter footer ]
48 |
49 |
50 | boxFooter : Maybe (String, String) -> Html msg
51 | boxFooter maybeFooter =
52 | case maybeFooter of
53 | Nothing ->
54 | text ""
55 |
56 | Just (path, description) ->
57 | a [ href path
58 | , title description
59 | ]
60 | [ div [ class "box-footer" ] [ Icon.plus ]
61 | ]
62 |
--------------------------------------------------------------------------------
/reactor/src/NotFound.elm:
--------------------------------------------------------------------------------
1 | module NotFound exposing (main)
2 |
3 |
4 | import Browser
5 | import Html exposing (..)
6 | import Html.Attributes exposing (..)
7 |
8 |
9 |
10 | main : Program () () ()
11 | main =
12 | Browser.document
13 | { init = \_ -> ((), Cmd.none)
14 | , update = \_ _ -> ((), Cmd.none)
15 | , subscriptions = \_ -> Sub.none
16 | , view = \_ -> page
17 | }
18 |
19 |
20 | page : Browser.Document ()
21 | page =
22 | { title = "Page not found"
23 | , body =
24 | [ div [ class "not-found" ]
25 | [ div [ style "font-size" "12em" ] [ text "404" ]
26 | , div [ style "font-size" "3em" ] [ text "Page not found" ]
27 | ]
28 | ]
29 | }
--------------------------------------------------------------------------------
/reactor/src/mock.txt:
--------------------------------------------------------------------------------
1 | # Dependency Explorer
2 |
3 | Mass Updates: | RESET | PATCH | MINOR | MAJOR |
4 |
5 | ⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣿⣇ ←→
6 |
7 | DEPENDENCIES
8 |
9 | DIRECT
10 | NoRedInk/elm-json-decode-pipeline 1.0.0 → 3.0.0 (MAJOR)
11 | elm/browser 1.0.0 → 1.0.2 (MINOR)
12 | elm/core 1.0.0 → 1.0.5 (CUSTOM: 1.0.0 <= v < 2.0.0)
13 | elm/html 1.0.0 → 6.0.2 (ANY)
14 | elm/http 1.0.0 → 1.0.0 (LOCKED)
15 | elm/json 1.0.0 → 1.0.0 (LOCKED)
16 | elm/time 1.0.0 → 1.0.0 (LOCKED)
17 | elm/url 1.0.0 → 1.0.0 (LOCKED)
18 | elm-explorations/markdown 1.0.0 → 1.0.0 (LOCKED)
19 | rtfeldman/elm-iso8601-date-strings 1.1.0 → (REMOVE)
20 | ADD
21 |
22 | INDIRECT
23 | elm/parser 1.0.0 → 1.0.0 (LOCKED)
24 | elm/virtual-dom 1.0.0 → 1.0.0 (LOCKED)
25 |
26 | TEST DEPENDENCIES
27 |
28 | DIRECT
29 | elm-explorations/test 1.0.0 → 1.0.0 (LOCKED)
30 | ADD
31 |
32 | INDIRECT
33 | elm/random 1.0.0 → 1.0.0 (LOCKED)
34 |
--------------------------------------------------------------------------------
/roadmap.md:
--------------------------------------------------------------------------------
1 | # Roadmap
2 |
3 | If you like what you see now, that's pretty much what Elm is going to be for a while.
4 |
5 | I'm currently doing some exploratory work with compiler techniques and targets, but it is still too early to tell which parts of that might work out. I know many readers take "exploring X" as a promise that "X will definitely happen", so I want to be careful in setting expectations low since there is still so much uncertainty in the work. So while there is no real news to share at this point, I can give some expectations about future stability of Elm:
6 |
7 | **If this exploratory work goes well**, even in the wildest version of success, I wouldn't expect the language or core packages to change very much. Maybe two or three years down the line it could reveal something that'd be good to fix up, but I don't really foresee notable changes right now.
8 |
9 | **If this exploratory work does not work out**, I have some more conservative projects that are good ideas that I want to circle back to at some point. Things like:
10 |
11 | - add a constrained type variable `eq` to get rid of the runtime error for `(==)` on functions
12 | - try to integrate `elm test` so it can run only tests that changed
13 | - try to get `elm format` using the latest parsing infrastructure so it's not a perf bottleneck during development anymore
14 | - do another round on perf because I have one last idea that can squeeze out a bit more speed
15 |
16 | These are all nice quality of life things, but with 0.19.0 and 0.19.1 taking so long, I got pretty burnt out on "incremental improvements that are good ideas, but take a very long time and aren't very exciting to non-Elm users." Without getting too into the details, I really needed to change things up before returning to these particular ideas.
17 |
18 | If someone has a security issue from the compiler or core libraries, please DM me about it on the Elm Slack. Outside of security issues, I think capacity for more discretionary changes will increase once the lessons from the compiler explorations become more clear. Hopefully the discussion [here](https://discourse.elm-lang.org/t/costs-funding-in-open-source-languages/5722) clarifies the thinking on these capacity questions a bit.
19 |
20 |
21 |
22 |
23 | **Taking a step back**, I have found that working in this looser style has produced a high baseline of quality, and I think that is an important part of Elm. For example, all the error message work in Elm began as a project to implement the `--report=json` flag. That work happened to reveal some cool ideas on improving error messages, and if I had been using a rigid roadmap, I might have skipped those ideas to meet the publicly-stated arbitrary deadline. We'd have a clearer roadmap, but error messages no different than other type-inferred languages.
24 |
25 | So I think having more flexibility in planning is a competitive advantage for Elm in certain ways, but it is obviously a trade off that does not work for everyone. If someone needs more certainty, I generally recommend looking into other languages to see if they have a balance that works better for their needs. For example, languages made by big corporations are generally "less risky" on things like this, but I think you see the fruits of that kind of process in the design decisions as well. Trade offs!
26 |
27 | Anyway, like I said at the beginning, if you like what you see now, that's what it's going to be for a while. Even in the best case scenario with my current explorations!
28 |
29 | I hope this is helpful information, and I hope you have a good experience with Elm, even if you ultimately find a different language that works better for you!
30 |
--------------------------------------------------------------------------------
/terminal/impl/Terminal/Helpers.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE OverloadedStrings #-}
2 | module Terminal.Helpers
3 | ( version
4 | , elmFile
5 | , package
6 | )
7 | where
8 |
9 |
10 | import qualified Data.ByteString.UTF8 as BS_UTF8
11 | import qualified Data.Char as Char
12 | import qualified Data.List as List
13 | import qualified Data.Map as Map
14 | import qualified Data.Utf8 as Utf8
15 | import qualified System.FilePath as FP
16 |
17 | import Terminal (Parser(..))
18 | import qualified Deps.Registry as Registry
19 | import qualified Elm.Package as Pkg
20 | import qualified Elm.Version as V
21 | import qualified Parse.Primitives as P
22 | import qualified Stuff
23 | import qualified Reporting.Suggest as Suggest
24 |
25 |
26 |
27 | -- VERSION
28 |
29 |
30 | version :: Parser V.Version
31 | version =
32 | Parser
33 | { _singular = "version"
34 | , _plural = "versions"
35 | , _parser = parseVersion
36 | , _suggest = suggestVersion
37 | , _examples = return . exampleVersions
38 | }
39 |
40 |
41 | parseVersion :: String -> Maybe V.Version
42 | parseVersion chars =
43 | case P.fromByteString V.parser (,) (BS_UTF8.fromString chars) of
44 | Right vsn -> Just vsn
45 | Left _ -> Nothing
46 |
47 |
48 | suggestVersion :: String -> IO [String]
49 | suggestVersion _ =
50 | return []
51 |
52 |
53 | exampleVersions :: String -> [String]
54 | exampleVersions chars =
55 | let
56 | chunks = map Utf8.toChars (Utf8.split 0x2E {-.-} (Utf8.fromChars chars))
57 | isNumber cs = not (null cs) && all Char.isDigit cs
58 | in
59 | if all isNumber chunks then
60 | case chunks of
61 | [x] -> [ x ++ ".0.0" ]
62 | [x,y] -> [ x ++ "." ++ y ++ ".0" ]
63 | x:y:z:_ -> [ x ++ "." ++ y ++ "." ++ z ]
64 | _ -> ["1.0.0", "2.0.3"]
65 |
66 | else
67 | ["1.0.0", "2.0.3"]
68 |
69 |
70 |
71 | -- ELM FILE
72 |
73 |
74 | elmFile :: Parser FilePath
75 | elmFile =
76 | Parser
77 | { _singular = "elm file"
78 | , _plural = "elm files"
79 | , _parser = parseElmFile
80 | , _suggest = \_ -> return []
81 | , _examples = exampleElmFiles
82 | }
83 |
84 |
85 | parseElmFile :: String -> Maybe FilePath
86 | parseElmFile chars =
87 | if FP.takeExtension chars == ".elm" then
88 | Just chars
89 | else
90 | Nothing
91 |
92 |
93 | exampleElmFiles :: String -> IO [String]
94 | exampleElmFiles _ =
95 | return ["Main.elm","src/Main.elm"]
96 |
97 |
98 |
99 | -- PACKAGE
100 |
101 |
102 | package :: Parser Pkg.Name
103 | package =
104 | Parser
105 | { _singular = "package"
106 | , _plural = "packages"
107 | , _parser = parsePackage
108 | , _suggest = suggestPackages
109 | , _examples = examplePackages
110 | }
111 |
112 |
113 | parsePackage :: String -> Maybe Pkg.Name
114 | parsePackage chars =
115 | case P.fromByteString Pkg.parser (,) (BS_UTF8.fromString chars) of
116 | Right pkg -> Just pkg
117 | Left _ -> Nothing
118 |
119 |
120 | suggestPackages :: String -> IO [String]
121 | suggestPackages given =
122 | do cache <- Stuff.getPackageCache
123 | maybeRegistry <- Registry.read cache
124 | return $
125 | case maybeRegistry of
126 | Nothing ->
127 | []
128 |
129 | Just (Registry.Registry _ versions) ->
130 | filter (List.isPrefixOf given) $
131 | map Pkg.toChars (Map.keys versions)
132 |
133 |
134 | examplePackages :: String -> IO [String]
135 | examplePackages given =
136 | do cache <- Stuff.getPackageCache
137 | maybeRegistry <- Registry.read cache
138 | return $
139 | case maybeRegistry of
140 | Nothing ->
141 | [ "elm/json"
142 | , "elm/http"
143 | , "elm/random"
144 | ]
145 |
146 | Just (Registry.Registry _ versions) ->
147 | map Pkg.toChars $ take 4 $
148 | Suggest.sort given Pkg.toChars (Map.keys versions)
149 |
--------------------------------------------------------------------------------
/terminal/impl/Terminal/Internal.hs:
--------------------------------------------------------------------------------
1 | {-# LANGUAGE GADTs #-}
2 | module Terminal.Internal
3 | ( Command(..)
4 | , toName
5 | , Summary(..)
6 | , Flags(..)
7 | , Flag(..)
8 | , Parser(..)
9 | , Args(..)
10 | , CompleteArgs(..)
11 | , RequiredArgs(..)
12 | )
13 | where
14 |
15 |
16 | import Text.PrettyPrint.ANSI.Leijen (Doc)
17 |
18 |
19 |
20 | -- COMMAND
21 |
22 |
23 | data Command where
24 | Command
25 | :: String
26 | -> Summary
27 | -> String
28 | -> Doc
29 | -> Args args
30 | -> Flags flags
31 | -> (args -> flags -> IO ())
32 | -> Command
33 |
34 |
35 | toName :: Command -> String
36 | toName (Command name _ _ _ _ _ _) =
37 | name
38 |
39 |
40 |
41 | {-| The information that shows when you run the executable with no arguments.
42 | If you say it is `Common`, you need to tell people what it does. Try to keep
43 | it to two or three lines. If you say it is `Uncommon` you can rely on `Details`
44 | for a more complete explanation.
45 | -}
46 | data Summary = Common String | Uncommon
47 |
48 |
49 |
50 | -- FLAGS
51 |
52 |
53 | data Flags a where
54 | FDone :: a -> Flags a
55 | FMore :: Flags (a -> b) -> Flag a -> Flags b
56 |
57 |
58 | data Flag a where
59 | Flag :: String -> Parser a -> String -> Flag (Maybe a)
60 | OnOff :: String -> String -> Flag Bool
61 |
62 |
63 |
64 | -- PARSERS
65 |
66 |
67 | data Parser a =
68 | Parser
69 | { _singular :: String
70 | , _plural :: String
71 | , _parser :: String -> Maybe a
72 | , _suggest :: String -> IO [String]
73 | , _examples :: String -> IO [String]
74 | }
75 |
76 |
77 |
78 | -- ARGS
79 |
80 |
81 | newtype Args a =
82 | Args [CompleteArgs a]
83 |
84 |
85 | data CompleteArgs args where
86 | Exactly :: RequiredArgs args -> CompleteArgs args
87 | Multiple :: RequiredArgs ([a] -> args) -> Parser a -> CompleteArgs args
88 | Optional :: RequiredArgs (Maybe a -> args) -> Parser a -> CompleteArgs args
89 |
90 |
91 | data RequiredArgs a where
92 | Done :: a -> RequiredArgs a
93 | Required :: RequiredArgs (a -> b) -> Parser a -> RequiredArgs b
94 |
--------------------------------------------------------------------------------
/terminal/src/Develop/Generate/Help.hs:
--------------------------------------------------------------------------------
1 | {-# OPTIONS_GHC -Wall #-}
2 | {-# LANGUAGE OverloadedStrings #-}
3 | {-# LANGUAGE QuasiQuotes #-}
4 | module Develop.Generate.Help
5 | ( makePageHtml
6 | , makeCodeHtml
7 | )
8 | where
9 |
10 |
11 | import qualified Data.ByteString.Builder as B
12 | import Data.Monoid ((<>))
13 | import qualified Data.Name as Name
14 | import Text.RawString.QQ (r)
15 |
16 | import qualified Json.Encode as Encode
17 |
18 |
19 |
20 | -- PAGES
21 |
22 |
23 | makePageHtml :: Name.Name -> Maybe Encode.Value -> B.Builder
24 | makePageHtml moduleName maybeFlags =
25 | [r|
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
36 |
37 |
38 | |]
39 |
40 |
41 |
42 | -- CODE
43 |
44 |
45 | makeCodeHtml :: FilePath -> B.Builder -> B.Builder
46 | makeCodeHtml title code =
47 | [r|
48 |
49 |
50 |
51 | |] <> B.stringUtf8 title <> [r|
52 |
57 |
58 |
59 |
60 |
61 |
62 |