├── .gitignore
├── README.md
├── src
├── Elm
│ ├── Kernel
│ │ ├── Process.server.js
│ │ ├── Process.js
│ │ ├── Bitwise.js
│ │ ├── Char.js
│ │ ├── Basics.js
│ │ ├── List.js
│ │ ├── Error.js
│ │ ├── JsArray.js
│ │ ├── Scheduler.js
│ │ ├── Utils.js
│ │ ├── Debug.js
│ │ ├── String.js
│ │ └── Platform.js
│ └── JsArray.elm
├── Bitwise.elm
├── Platform
│ ├── Cmd.elm
│ └── Sub.elm
├── Tuple.elm
├── Debug.elm
├── Platform.elm
├── Process.elm
├── Set.elm
├── Char.elm
├── Maybe.elm
├── Result.elm
├── Task.elm
└── String.elm
├── hints
├── 3.md
├── 4.md
├── 2.md
├── 8.md
├── 11.md
├── 9.md
├── 5.md
├── 0.md
├── 10.md
├── 1.md
└── 6.md
├── tests
├── run-tests.sh
├── elm-package.json
├── Test
│ ├── Tuple.elm
│ ├── Equality.elm
│ ├── Result.elm
│ ├── CodeGen.elm
│ ├── Bitwise.elm
│ ├── Char.elm
│ ├── Dict.elm
│ ├── String.elm
│ ├── Maybe.elm
│ ├── List.elm
│ ├── Set.elm
│ ├── Array.elm
│ └── Basics.elm
└── Main.elm
├── elm.json
├── .travis.yml
├── LICENSE
├── .github
└── CONTRIBUTING.md
└── changelog.md
/.gitignore:
--------------------------------------------------------------------------------
1 | elm-stuff
2 | tests/test.js
3 | node_modules/
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Moved to [`elm/core`](https://github.com/elm/core)
2 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Process.server.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Scheduler exposing (binding)
4 |
5 | */
6 |
7 |
8 | function _Process_sleep()
9 | {
10 | return __Scheduler_binding(function() {});
11 | }
12 |
--------------------------------------------------------------------------------
/hints/3.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | Your application has multiple ports with the same name.
13 |
14 | You can get more detailed information if you can reproduce the error in dev mode.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/hints/4.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | You are trying to send an invalid value through a port.
13 |
14 | You can get more detailed information if you can reproduce the error in dev mode.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Process.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Scheduler exposing (binding, succeed)
4 | import Elm.Kernel.Utils exposing (Tuple0)
5 |
6 | */
7 |
8 |
9 | function _Process_sleep(time)
10 | {
11 | return __Scheduler_binding(function(callback) {
12 | var id = setTimeout(function() {
13 | callback(__Scheduler_succeed(__Utils_Tuple0));
14 | }, time);
15 |
16 | return function() { clearTimeout(id); };
17 | });
18 | }
19 |
--------------------------------------------------------------------------------
/hints/2.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | When you initialize your Elm application, you are providing bad flags.
13 |
14 | You can get more detailed information if you can reproduce the error in dev mode.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/tests/run-tests.sh:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 |
3 | cd "$(dirname "$0")"
4 | set -e
5 |
6 |
7 | elm-package install -y
8 |
9 | VERSION_DIR="$(ls elm-stuff/packages/elm-lang/core/)"
10 | CORE_PACKAGE_DIR="elm-stuff/packages/elm-lang/core/$VERSION_DIR"
11 | CORE_GIT_DIR="$(dirname $PWD)"
12 |
13 | echo "Linking $CORE_PACKAGE_DIR to $CORE_GIT_DIR"
14 | rm -rf $CORE_PACKAGE_DIR
15 | ln -s $CORE_GIT_DIR $CORE_PACKAGE_DIR
16 |
17 | elm-make --yes --output test.js Main.elm
18 |
19 | elm-test Main.elm
20 |
--------------------------------------------------------------------------------
/hints/8.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | Your application is calling `Debug.crash` at some point. The call may be in your code or in a dependency.
13 |
14 | You can get more detailed information on who is calling `Debug.crash` if you can reproduce this error in dev mode.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/hints/11.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | Your application is calling `n % 0` at some point, causing a crash. This may be happening in your code or in a dependency.
13 |
14 | You can get more detailed information about when this happens if you can reproduce this error in dev mode.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/hints/9.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | Your application is calling `Debug.crash` in a `case` at some point. The call may be in your code or in a dependency.
13 |
14 | You can get more detailed information on who is calling `Debug.crash` if you can reproduce this error in dev mode.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/tests/elm-package.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.1.1",
3 | "summary": "Tests for Elm's standard libraries",
4 | "repository": "http://github.com/elm-lang/core.git",
5 | "license": "BSD3",
6 | "source-directories": [
7 | ".",
8 | "../src"
9 | ],
10 | "exposed-modules": [ ],
11 | "native-modules": true,
12 | "dependencies": {
13 | "elm-community/elm-test": "3.1.0 <= v < 4.0.0",
14 | "rtfeldman/node-test-runner": "3.0.0 <= v < 4.0.0"
15 | },
16 | "elm-version": "0.18.0 <= v < 0.19.0"
17 | }
18 |
--------------------------------------------------------------------------------
/hints/5.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | Trying to use `(==)` on functions. There is no way to know if functions are "the same" in the Elm sense. You can read more about why it works this way [here](http://package.elm-lang.org/packages/elm-lang/core/latest/Basics#==). The root cause may be in your code or in a dependency.
13 |
14 | You can get more detailed information on when this happens if you can reproduce the error in dev mode.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Bitwise.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | */
4 |
5 |
6 | var _Bitwise_and = F2(function(a, b)
7 | {
8 | return a & b;
9 | });
10 |
11 | var _Bitwise_or = F2(function(a, b)
12 | {
13 | return a | b;
14 | });
15 |
16 | var _Bitwise_xor = F2(function(a, b)
17 | {
18 | return a ^ b;
19 | });
20 |
21 | function _Bitwise_complement(a)
22 | {
23 | return ~a;
24 | };
25 |
26 | var _Bitwise_shiftLeftBy = F2(function(offset, a)
27 | {
28 | return a << offset;
29 | });
30 |
31 | var _Bitwise_shiftRightBy = F2(function(offset, a)
32 | {
33 | return a >> offset;
34 | });
35 |
36 | var _Bitwise_shiftRightZfBy = F2(function(offset, a)
37 | {
38 | return a >>> offset;
39 | });
40 |
--------------------------------------------------------------------------------
/hints/0.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | Your application is triggering a bug in the `Dict` module of this library! The root cause may be in your code or in a dependency.
13 |
14 | Try to reproduce the issue in dev mode. From there, create an [SSCCE](http://sscce.org/) that reproduces the issue and report it [here](https://github.com/elm-lang/core/issues) with a title like “Dict bug: ...” and a description that explains your understanding of the problem.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/hints/10.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | Your application is running into a bug in `elm-lang/virtual-dom` at some point. The root cause may be in your code or in a dependency.
13 |
14 | Try to reproduce the issue in dev mode. From there, create an [SSCCE](http://sscce.org/) that reproduces the issue and report it [here](https://github.com/elm-lang/virtual-dom/issues) with a title like “Implementation bug: ...” and a description that explains your understanding of the problem.
15 |
16 | Hopefully that helps!
17 |
--------------------------------------------------------------------------------
/hints/1.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | The application transitioned to a new URL, but the `Url.Parser.toUrl` function could not handle it. It probably is an unsupported URL like a `file://` or `data://` URL but it may be a bug in the [`elm-lang/url`][url] package. Explore more and report an [SSCCE](http://sscce.org/) if you can definitively show it is a bug.
13 |
14 | You can get more detailed information if you can reproduce the error in dev mode.
15 |
16 | Hopefully that helps!
17 |
18 | [url]: https://github.com/elm-lang/url
19 |
--------------------------------------------------------------------------------
/elm.json:
--------------------------------------------------------------------------------
1 | {
2 | "type": "package",
3 | "name": "elm-lang/core",
4 | "summary": "Elm's standard libraries",
5 | "license": "BSD-3-Clause",
6 | "version": "6.0.0",
7 | "exposed-modules": {
8 | "Primitives": [
9 | "Basics",
10 | "String",
11 | "Char",
12 | "Bitwise",
13 | "Tuple"
14 | ],
15 | "Collections": [
16 | "List",
17 | "Dict",
18 | "Set",
19 | "Array"
20 | ],
21 | "Error Handling": [
22 | "Maybe",
23 | "Result"
24 | ],
25 | "Debug": [
26 | "Debug"
27 | ],
28 | "Effects": [
29 | "Platform",
30 | "Platform.Cmd",
31 | "Platform.Sub",
32 | "Task",
33 | "Process"
34 | ]
35 | },
36 | "elm-version": "0.19.0 <= v < 0.20.0",
37 | "dependencies": {},
38 | "test-dependencies": {}
39 | }
--------------------------------------------------------------------------------
/hints/6.md:
--------------------------------------------------------------------------------
1 | # Problem
2 |
3 | The application you are using ran into a problem.
4 |
5 | If you let their support team know about this error, please give them a link to this web page as well!
6 |
7 |
8 |
9 |
10 | ## Info for Developers
11 |
12 | This page loads multiple Elm scripts. It then puts the publicly exposed modules from each script on the global `Elm` object. The problem is that there is a name clash.
13 |
14 | For example, script `a.js` may expose an Elm module named `Main`, and script `b.js` may expose an Elm module named `Main`. Elm cannot know which one to use! So they need to have unique names if you need them to be added to the global `Elm` object.
15 |
16 | Maybe a duplicate script is getting loaded accidentally? Anyway, if you reproduce this error in dev mode, it will tell you the module name that is getting added more than once.
17 |
18 | Hopefully that helps!
19 |
--------------------------------------------------------------------------------
/tests/Test/Tuple.elm:
--------------------------------------------------------------------------------
1 | module Test.Tuple exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Tuple exposing (..)
5 | import Test exposing (..)
6 | import Expect
7 |
8 |
9 | tests : Test
10 | tests =
11 | describe "Tuple Tests"
12 | [ describe "first"
13 | [ test "extracts first element" <|
14 | \() -> Expect.equal 1 (first ( 1, 2 ))
15 | ]
16 | , describe "second"
17 | [ test "extracts second element" <|
18 | \() -> Expect.equal 2 (second ( 1, 2 ))
19 | ]
20 | , describe "mapFirst"
21 | [ test "applies function to first element" <|
22 | \() -> Expect.equal ( 5, 1 ) (mapFirst ((*) 5) ( 1, 1 ))
23 | ]
24 | , describe "mapSecond"
25 | [ test "applies function to second element" <|
26 | \() -> Expect.equal ( 1, 5 ) (mapSecond ((*) 5) ( 1, 1 ))
27 | ]
28 | ]
29 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Char.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Utils exposing (chr)
4 |
5 | */
6 |
7 |
8 | function _Char_toCode(char)
9 | {
10 | var code = char.charCodeAt(0);
11 | if (0xD800 <= code && code <= 0xDBFF)
12 | {
13 | return (code - 0xD800) * 0x400 + char.charCodeAt(1) - 0xDC00 + 0x10000
14 | }
15 | return code;
16 | }
17 |
18 | function _Char_fromCode(code)
19 | {
20 | return __Utils_chr(
21 | (code < 0 || 0x10FFFF < code)
22 | ? '\uFFFD'
23 | :
24 | (code <= 0xFFFF)
25 | ? String.fromCharCode(code)
26 | :
27 | (code -= 0x10000,
28 | String.fromCharCode(Math.floor(code / 0x400) + 0xD800)
29 | +
30 | String.fromCharCode(code % 0x400 + 0xDC00)
31 | )
32 | );
33 | }
34 |
35 | function _Char_toUpper(char)
36 | {
37 | return __Utils_chr(char.toUpperCase());
38 | }
39 |
40 | function _Char_toLower(char)
41 | {
42 | return __Utils_chr(char.toLowerCase());
43 | }
44 |
45 | function _Char_toLocaleUpper(char)
46 | {
47 | return __Utils_chr(char.toLocaleUpperCase());
48 | }
49 |
50 | function _Char_toLocaleLower(char)
51 | {
52 | return __Utils_chr(char.toLocaleLowerCase());
53 | }
54 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | cache:
4 | directories:
5 | - test/elm-stuff/build-artifacts
6 | - sysconfcpus
7 |
8 | language: node_js
9 |
10 | node_js:
11 | - "4.3"
12 |
13 | before_install:
14 | - if [ ${TRAVIS_OS_NAME} == "osx" ];
15 | then brew update; brew install nvm; mkdir ~/.nvm; export NVM_DIR=~/.nvm; source $(brew --prefix nvm)/nvm.sh;
16 | fi
17 | - echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
18 | - | # epic build time improvement - see https://github.com/elm-lang/elm-compiler/issues/1473#issuecomment-245704142
19 | if [ ! -d sysconfcpus/bin ];
20 | then
21 | git clone https://github.com/obmarg/libsysconfcpus.git;
22 | cd libsysconfcpus;
23 | ./configure --prefix=$TRAVIS_BUILD_DIR/sysconfcpus;
24 | make && make install;
25 | cd ..;
26 | fi
27 |
28 | install:
29 | - npm install -g elm@0.18 elm-test
30 | - mv $(npm config get prefix)/bin/elm-make $(npm config get prefix)/bin/elm-make-old
31 | - printf '%s\n\n' '#!/bin/bash' 'echo "Running elm-make with sysconfcpus -n 2"' '$TRAVIS_BUILD_DIR/sysconfcpus/bin/sysconfcpus -n 2 elm-make-old "$@"' > $(npm config get prefix)/bin/elm-make
32 | - chmod +x $(npm config get prefix)/bin/elm-make
33 |
34 | script:
35 | - bash tests/run-tests.sh
36 |
--------------------------------------------------------------------------------
/tests/Main.elm:
--------------------------------------------------------------------------------
1 | port module Main exposing (..)
2 |
3 | import Basics exposing (..)
4 | import Task exposing (..)
5 | import Test exposing (..)
6 | import Platform.Cmd exposing (Cmd)
7 | import Json.Decode exposing (Value)
8 | import Test.Runner.Node exposing (run, TestProgram)
9 | import Test.Array as Array
10 | import Test.Basics as Basics
11 | import Test.Bitwise as Bitwise
12 | import Test.Char as Char
13 | import Test.CodeGen as CodeGen
14 | import Test.Dict as Dict
15 | import Test.Maybe as Maybe
16 | import Test.Equality as Equality
17 | import Test.Json as Json
18 | import Test.List as List
19 | import Test.Result as Result
20 | import Test.Set as Set
21 | import Test.String as String
22 | import Test.Tuple as Tuple
23 |
24 |
25 | tests : Test
26 | tests =
27 | describe "Elm Standard Library Tests"
28 | [ Array.tests
29 | , Basics.tests
30 | , Bitwise.tests
31 | , Char.tests
32 | , CodeGen.tests
33 | , Dict.tests
34 | , Equality.tests
35 | , List.tests
36 | , Result.tests
37 | , Set.tests
38 | , String.tests
39 | , Maybe.tests
40 | , Tuple.tests
41 | ]
42 |
43 |
44 | main : TestProgram
45 | main =
46 | run emit tests
47 |
48 |
49 | port emit : ( String, Value ) -> Cmd msg
50 |
--------------------------------------------------------------------------------
/tests/Test/Equality.elm:
--------------------------------------------------------------------------------
1 | module Test.Equality exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Maybe exposing (..)
5 | import Test exposing (..)
6 | import Expect
7 |
8 |
9 | type Different
10 | = A String
11 | | B (List Int)
12 |
13 |
14 | tests : Test
15 | tests =
16 | let
17 | diffTests =
18 | describe "ADT equality"
19 | [ test "As eq" <| \() -> Expect.equal True (A "a" == A "a")
20 | , test "Bs eq" <| \() -> Expect.equal True (B [ 1 ] == B [ 1 ])
21 | , test "A left neq" <| \() -> Expect.equal True (A "a" /= B [ 1 ])
22 | , test "A left neq" <| \() -> Expect.equal True (B [ 1 ] /= A "a")
23 | ]
24 |
25 | recordTests =
26 | describe "Record equality"
27 | [ test "empty same" <| \() -> Expect.equal True ({} == {})
28 | , test "ctor same" <| \() -> Expect.equal True ({ field = Just 3 } == { field = Just 3 })
29 | , test "ctor same, special case" <| \() -> Expect.equal True ({ ctor = Just 3 } == { ctor = Just 3 })
30 | , test "ctor diff" <| \() -> Expect.equal True ({ field = Just 3 } /= { field = Nothing })
31 | , test "ctor diff, special case" <| \() -> Expect.equal True ({ ctor = Just 3 } /= { ctor = Nothing })
32 | ]
33 | in
34 | describe "Equality Tests" [ diffTests, recordTests ]
35 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2014-present, Evan Czaplicki
2 |
3 | All rights reserved.
4 |
5 | Redistribution and use in source and binary forms, with or without
6 | modification, are permitted provided that the following conditions are met:
7 |
8 | * Redistributions of source code must retain the above copyright
9 | notice, this list of conditions and the following disclaimer.
10 |
11 | * Redistributions in binary form must reproduce the above
12 | copyright notice, this list of conditions and the following
13 | disclaimer in the documentation and/or other materials provided
14 | with the distribution.
15 |
16 | * Neither the name of Evan Czaplicki nor the names of other
17 | contributors may be used to endorse or promote products derived
18 | from this software without specific prior written permission.
19 |
20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Basics.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Error exposing (throw)
4 |
5 | */
6 |
7 |
8 | // MATH
9 |
10 | var _Basics_add = F2(function(a, b) { return a + b; });
11 | var _Basics_sub = F2(function(a, b) { return a - b; });
12 | var _Basics_mul = F2(function(a, b) { return a * b; });
13 | var _Basics_fdiv = F2(function(a, b) { return a / b; });
14 | var _Basics_idiv = F2(function(a, b) { return (a / b) | 0; });
15 | var _Basics_pow = F2(Math.pow);
16 |
17 | var _Basics_remainderBy = F2(function(b, a) { return a % b; });
18 |
19 | // https://www.microsoft.com/en-us/research/wp-content/uploads/2016/02/divmodnote-letter.pdf
20 | var _Basics_modBy = F2(function(modulus, x)
21 | {
22 | var answer = x % modulus;
23 | return modulus === 0
24 | ? __Error_throw(11)
25 | :
26 | ((answer > 0 && modulus < 0) || (answer < 0 && modulus > 0))
27 | ? answer + modulus
28 | : answer;
29 | });
30 |
31 |
32 | // TRIGONOMETRY
33 |
34 | var _Basics_pi = Math.PI;
35 | var _Basics_e = Math.E;
36 | var _Basics_cos = Math.cos;
37 | var _Basics_sin = Math.sin;
38 | var _Basics_tan = Math.tan;
39 | var _Basics_acos = Math.acos;
40 | var _Basics_asin = Math.asin;
41 | var _Basics_atan = Math.atan;
42 | var _Basics_atan2 = F2(Math.atan2);
43 |
44 |
45 | // MORE MATH
46 |
47 | function _Basics_toFloat(x) { return x; }
48 | function _Basics_truncate(n) { return n | 0; }
49 | function _Basics_isInfinite(n) { return n === Infinity || n === -Infinity; }
50 |
51 | var _Basics_ceiling = Math.ceil;
52 | var _Basics_floor = Math.floor;
53 | var _Basics_round = Math.round;
54 | var _Basics_sqrt = Math.sqrt;
55 | var _Basics_log = Math.log;
56 | var _Basics_isNaN = isNaN;
57 |
58 |
59 | // BOOLEANS
60 |
61 | function _Basics_not(bool) { return !bool; }
62 | var _Basics_and = F2(function(a, b) { return a && b; });
63 | var _Basics_or = F2(function(a, b) { return a || b; });
64 | var _Basics_xor = F2(function(a, b) { return a !== b; });
65 |
--------------------------------------------------------------------------------
/.github/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to the core libraries
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 | ## Ground rules
8 |
9 | * Always make pull requests minimal. If it can be split up, it should be split up.
10 | * Use style consistent with the file you are modifying.
11 | * Use descriptive titles for PRs
12 | * Provide all the necessary context for evaluation in the PR.
13 | If there are relevant issues or examples or discussions, add them.
14 | If things can be summarized, summarize them. The easiest PRs are ones
15 | that already address the reviewers questions and concerns.
16 |
17 | ## Documentation Fixes
18 |
19 | If you want to fix docs, just open a PR. This is super helpful!
20 |
21 | ## Bug Fixes
22 |
23 | If you find an issue or see one you want to work on, go for it!
24 |
25 | The best strategy is often to dive in. Asking for directions usually
26 | does not work. If someone knew the specifics and knew how how to fix
27 | it, it is likely they would have already sent the PR themselves!
28 |
29 | Also, be sure you are testing.
30 |
31 | ## Adding New Functions
32 |
33 | We are fairly conservative about adding new functions to core libraries.
34 | If you want to augment the `List` or `Array` library, we recommend creating
35 | small packages called `list-extras` or `array-extras` that have all the
36 | features you want. There are already several such packages maintained at
37 | the [Elm Community organization](https://github.com/elm-community) that
38 | welcome contributions in the form of pull requests.
39 |
40 | Long term, we will set up a process to review `*-extras` packages to move
41 | stuff into core. By going through packages, it will be much easier to assess
42 | whether a function is pleasant and useful in practice before committing to it
43 | in the core libraries.
44 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/List.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Utils exposing (cmp)
4 | import Basics exposing (EQ, LT)
5 |
6 | */
7 |
8 |
9 | var _List_Nil__PROD = { $: 0 };
10 | var _List_Nil__DEBUG = { $: '[]' };
11 |
12 | function _List_Cons__PROD(hd, tl) { return { $: 1, a: hd, b: tl }; }
13 | function _List_Cons__DEBUG(hd, tl) { return { $: '::', a: hd, b: tl }; }
14 |
15 |
16 | var _List_cons = F2(_List_Cons);
17 |
18 | function _List_fromArray(arr)
19 | {
20 | var out = _List_Nil;
21 | for (var i = arr.length; i--; )
22 | {
23 | out = _List_Cons(arr[i], out);
24 | }
25 | return out;
26 | }
27 |
28 | function _List_toArray(xs)
29 | {
30 | for (var out = []; xs.b; xs = xs.b) // WHILE_CONS
31 | {
32 | out.push(xs.a);
33 | }
34 | return out;
35 | }
36 |
37 | var _List_map2 = F3(function(f, xs, ys)
38 | {
39 | for (var arr = []; xs.b && ys.b; xs = xs.b, ys = ys.b) // WHILE_CONSES
40 | {
41 | arr.push(A2(f, xs.a, ys.a));
42 | }
43 | return _List_fromArray(arr);
44 | });
45 |
46 | var _List_map3 = F4(function(f, xs, ys, zs)
47 | {
48 | for (var arr = []; xs.b && ys.b && zs.b; xs = xs.b, ys = ys.b, zs = zs.b) // WHILE_CONSES
49 | {
50 | arr.push(A3(f, xs.a, ys.a, zs.a));
51 | }
52 | return _List_fromArray(arr);
53 | });
54 |
55 | var _List_map4 = F5(function(f, ws, xs, ys, zs)
56 | {
57 | for (var arr = []; ws.b && xs.b && ys.b && zs.b; ws = ws.b, xs = xs.b, ys = ys.b, zs = zs.b) // WHILE_CONSES
58 | {
59 | arr.push(A4(f, ws.a, xs.a, ys.a, zs.a));
60 | }
61 | return _List_fromArray(arr);
62 | });
63 |
64 | var _List_map5 = F6(function(f, vs, ws, xs, ys, zs)
65 | {
66 | for (var arr = []; vs.b && ws.b && xs.b && ys.b && zs.b; vs = vs.b, ws = ws.b, xs = xs.b, ys = ys.b, zs = zs.b) // WHILE_CONSES
67 | {
68 | arr.push(A5(f, vs.a, ws.a, xs.a, ys.a, zs.a));
69 | }
70 | return _List_fromArray(arr);
71 | });
72 |
73 | var _List_sortBy = F2(function(f, xs)
74 | {
75 | return _List_fromArray(_List_toArray(xs).sort(function(a, b) {
76 | return __Utils_cmp(f(a), f(b));
77 | }));
78 | });
79 |
80 | var _List_sortWith = F2(function(f, xs)
81 | {
82 | return _List_fromArray(_List_toArray(xs).sort(function(a, b) {
83 | var ord = A2(f, a, b);
84 | return ord === __Basics_EQ ? 0 : ord === __Basics_LT ? -1 : 1;
85 | }));
86 | });
87 |
--------------------------------------------------------------------------------
/src/Bitwise.elm:
--------------------------------------------------------------------------------
1 | module Bitwise exposing
2 | ( and, or, xor, complement
3 | , shiftLeftBy, shiftRightBy, shiftRightZfBy
4 | )
5 |
6 | {-| Library for [bitwise operations](http://en.wikipedia.org/wiki/Bitwise_operation).
7 |
8 | # Basic Operations
9 | @docs and, or, xor, complement
10 |
11 | # Bit Shifts
12 | @docs shiftLeftBy, shiftRightBy, shiftRightZfBy
13 | -}
14 |
15 |
16 | import Basics exposing (Int)
17 | import Elm.Kernel.Bitwise
18 |
19 |
20 |
21 | {-| Bitwise AND
22 | -}
23 | and : Int -> Int -> Int
24 | and =
25 | Elm.Kernel.Bitwise.and
26 |
27 |
28 | {-| Bitwise OR
29 | -}
30 | or : Int -> Int -> Int
31 | or =
32 | Elm.Kernel.Bitwise.or
33 |
34 |
35 | {-| Bitwise XOR
36 | -}
37 | xor : Int -> Int -> Int
38 | xor =
39 | Elm.Kernel.Bitwise.xor
40 |
41 |
42 | {-| Flip each bit individually, often called bitwise NOT
43 | -}
44 | complement : Int -> Int
45 | complement =
46 | Elm.Kernel.Bitwise.complement
47 |
48 |
49 | {-| Shift bits to the left by a given offset, filling new bits with zeros.
50 | This can be used to multiply numbers by powers of two.
51 |
52 | shiftLeftBy 1 5 == 10
53 | shiftLeftBy 5 1 == 32
54 | -}
55 | shiftLeftBy : Int -> Int -> Int
56 | shiftLeftBy =
57 | Elm.Kernel.Bitwise.shiftLeftBy
58 |
59 |
60 | {-| Shift bits to the right by a given offset, filling new bits with
61 | whatever is the topmost bit. This can be used to divide numbers by powers of two.
62 |
63 | shiftRightBy 1 32 == 16
64 | shiftRightBy 2 32 == 8
65 | shiftRightBy 1 -32 == -16
66 |
67 | This is called an [arithmetic right shift][ars], often written `>>`, and
68 | sometimes called a sign-propagating right shift because it fills empty spots
69 | with copies of the highest bit.
70 |
71 | [ars]: http://en.wikipedia.org/wiki/Bitwise_operation#Arithmetic_shift
72 | -}
73 | shiftRightBy : Int -> Int -> Int
74 | shiftRightBy =
75 | Elm.Kernel.Bitwise.shiftRightBy
76 |
77 |
78 | {-| Shift bits to the right by a given offset, filling new bits with zeros.
79 |
80 | shiftRightZfBy 1 32 == 16
81 | shiftRightZfBy 2 32 == 8
82 | shiftRightZfBy 1 -32 == 2147483632
83 |
84 | This is called an [logical right shift][lrs], often written `>>>`, and
85 | sometimes called a zero-fill right shift because it fills empty spots with
86 | zeros.
87 |
88 | [lrs]: http://en.wikipedia.org/wiki/Bitwise_operation#Logical_shift
89 | -}
90 | shiftRightZfBy : Int -> Int -> Int
91 | shiftRightZfBy =
92 | Elm.Kernel.Bitwise.shiftRightZfBy
93 |
94 |
--------------------------------------------------------------------------------
/src/Platform/Cmd.elm:
--------------------------------------------------------------------------------
1 | module Platform.Cmd exposing
2 | ( Cmd
3 | , none
4 | , batch
5 | , map
6 | )
7 |
8 | {-|
9 |
10 | > **Note:** Elm has **managed effects**, meaning that things like HTTP
11 | > requests or writing to disk are all treated as *data* in Elm. When this
12 | > data is given to the Elm runtime system, it can do some “query optimization”
13 | > before actually performing the effect. Perhaps unexpectedly, this managed
14 | > effects idea is the heart of why Elm is so nice for testing, reuse,
15 | > reproducibility, etc.
16 | >
17 | > Elm has two kinds of managed effects: commands and subscriptions.
18 |
19 | # Commands
20 | @docs Cmd, none, batch
21 |
22 | # Fancy Stuff
23 | @docs map
24 |
25 | -}
26 |
27 | import Elm.Kernel.Platform
28 |
29 |
30 |
31 | -- COMMANDS
32 |
33 |
34 | {-| A command is a way of telling Elm, “Hey, I want you to do this thing!”
35 | So if you want to send an HTTP request, you would need to command Elm to do it.
36 | Or if you wanted to ask for geolocation, you would need to command Elm to go
37 | get it.
38 |
39 | Every `Cmd` specifies (1) which effects you need access to and (2) the type of
40 | messages that will come back into your application.
41 |
42 | **Note:** Do not worry if this seems confusing at first! As with every Elm user
43 | ever, commands will make more sense as you work through [the Elm Architecture
44 | Tutorial](http://guide.elm-lang.org/architecture/) and see how they
45 | fit into a real application!
46 | -}
47 | type Cmd msg = Cmd
48 |
49 |
50 | {-| Tell the runtime that there are no commands.
51 |
52 | -}
53 | none : Cmd msg
54 | none =
55 | batch []
56 |
57 |
58 | {-| When you need the runtime system to perform a couple commands, you
59 | can batch them together. Each is handed to the runtime at the same time,
60 | and since each can perform arbitrary operations in the world, there are
61 | no ordering guarantees about the results.
62 |
63 | **Note:** `Cmd.none` and `Cmd.batch [ Cmd.none, Cmd.none ]` and `Cmd.batch []`
64 | all do the same thing.
65 | -}
66 | batch : List (Cmd msg) -> Cmd msg
67 | batch =
68 | Elm.Kernel.Platform.batch
69 |
70 |
71 |
72 | -- FANCY STUFF
73 |
74 |
75 | {-| Transform the messages produced by a command.
76 | Very similar to [`Html.map`](/packages/elm-lang/html/latest/Html#map).
77 |
78 | This is very rarely useful in well-structured Elm code, so definitely read the
79 | sections on [reuse][] and [modularity][] in the guide before reaching for this!
80 |
81 | [reuse]: https://guide.elm-lang.org/reuse/
82 | [modularity]: https://guide.elm-lang.org/modularity/
83 | -}
84 | map : (a -> msg) -> Cmd a -> Cmd msg
85 | map =
86 | Elm.Kernel.Platform.map
87 |
88 |
89 |
--------------------------------------------------------------------------------
/tests/Test/Result.elm:
--------------------------------------------------------------------------------
1 | module Test.Result exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Result
5 | import Result exposing (Result(..))
6 | import String
7 | import Test exposing (..)
8 | import Expect
9 |
10 |
11 | isEven n =
12 | if n % 2 == 0 then
13 | Ok n
14 | else
15 | Err "number is odd"
16 |
17 |
18 | add3 a b c =
19 | a + b + c
20 |
21 |
22 | add4 a b c d =
23 | a + b + c + d
24 |
25 |
26 | add5 a b c d e =
27 | a + b + c + d + e
28 |
29 |
30 | tests : Test
31 | tests =
32 | let
33 | mapTests =
34 | describe "map Tests"
35 | [ test "map Ok" <| \() -> Expect.equal (Ok 3) (Result.map ((+) 1) (Ok 2))
36 | , test "map Err" <| \() -> Expect.equal (Err "error") (Result.map ((+) 1) (Err "error"))
37 | ]
38 |
39 | mapNTests =
40 | describe "mapN Tests"
41 | [ test "map2 Ok" <| \() -> Expect.equal (Ok 3) (Result.map2 (+) (Ok 1) (Ok 2))
42 | , test "map2 Err" <| \() -> Expect.equal (Err "x") (Result.map2 (+) (Ok 1) (Err "x"))
43 | , test "map3 Ok" <| \() -> Expect.equal (Ok 6) (Result.map3 add3 (Ok 1) (Ok 2) (Ok 3))
44 | , test "map3 Err" <| \() -> Expect.equal (Err "x") (Result.map3 add3 (Ok 1) (Ok 2) (Err "x"))
45 | , test "map4 Ok" <| \() -> Expect.equal (Ok 10) (Result.map4 add4 (Ok 1) (Ok 2) (Ok 3) (Ok 4))
46 | , test "map4 Err" <| \() -> Expect.equal (Err "x") (Result.map4 add4 (Ok 1) (Ok 2) (Ok 3) (Err "x"))
47 | , test "map5 Ok" <| \() -> Expect.equal (Ok 15) (Result.map5 add5 (Ok 1) (Ok 2) (Ok 3) (Ok 4) (Ok 5))
48 | , test "map5 Err" <| \() -> Expect.equal (Err "x") (Result.map5 add5 (Ok 1) (Ok 2) (Ok 3) (Ok 4) (Err "x"))
49 | ]
50 |
51 | andThenTests =
52 | describe "andThen Tests"
53 | [ test "andThen Ok" <| \() -> Expect.equal (Ok 42) ((String.toInt "42") |> Result.andThen isEven)
54 | , test "andThen first Err" <|
55 | \() ->
56 | Expect.equal
57 | (Err "could not convert string '4.2' to an Int")
58 | (String.toInt "4.2" |> Result.andThen isEven)
59 | , test "andThen second Err" <|
60 | \() ->
61 | Expect.equal
62 | (Err "number is odd")
63 | (String.toInt "41" |> Result.andThen isEven)
64 | ]
65 | in
66 | describe "Result Tests"
67 | [ mapTests
68 | , mapNTests
69 | , andThenTests
70 | ]
71 |
--------------------------------------------------------------------------------
/src/Platform/Sub.elm:
--------------------------------------------------------------------------------
1 | module Platform.Sub exposing
2 | ( Sub
3 | , none
4 | , batch
5 | , map
6 | )
7 |
8 | {-|
9 |
10 | > **Note:** Elm has **managed effects**, meaning that things like HTTP
11 | > requests or writing to disk are all treated as *data* in Elm. When this
12 | > data is given to the Elm runtime system, it can do some “query optimization”
13 | > before actually performing the effect. Perhaps unexpectedly, this managed
14 | > effects idea is the heart of why Elm is so nice for testing, reuse,
15 | > reproducibility, etc.
16 | >
17 | > Elm has two kinds of managed effects: commands and subscriptions.
18 |
19 | # Subscriptions
20 | @docs Sub, none, batch
21 |
22 | # Fancy Stuff
23 | @docs map
24 | -}
25 |
26 | import Elm.Kernel.Platform
27 |
28 |
29 |
30 | -- SUBSCRIPTIONS
31 |
32 |
33 | {-| A subscription is a way of telling Elm, “Hey, let me know if anything
34 | interesting happens over there!” So if you want to listen for messages on a web
35 | socket, you would tell Elm to create a subscription. If you want to get clock
36 | ticks, you would tell Elm to subscribe to that. The cool thing here is that
37 | this means *Elm* manages all the details of subscriptions instead of *you*.
38 | So if a web socket goes down, *you* do not need to manually reconnect with an
39 | exponential backoff strategy, *Elm* does this all for you behind the scenes!
40 |
41 | Every `Sub` specifies (1) which effects you need access to and (2) the type of
42 | messages that will come back into your application.
43 |
44 | **Note:** Do not worry if this seems confusing at first! As with every Elm user
45 | ever, subscriptions will make more sense as you work through [the Elm Architecture
46 | Tutorial](http://guide.elm-lang.org/architecture/) and see how they fit
47 | into a real application!
48 | -}
49 | type Sub msg = Sub
50 |
51 |
52 | {-| Tell the runtime that there are no subscriptions.
53 | -}
54 | none : Sub msg
55 | none =
56 | batch []
57 |
58 |
59 | {-| When you need to subscribe to multiple things, you can create a `batch` of
60 | subscriptions.
61 |
62 | **Note:** `Sub.none` and `Sub.batch [ Sub.none, Sub.none ]` and
63 | `Sub.batch []` all do the same thing.
64 | -}
65 | batch : List (Sub msg) -> Sub msg
66 | batch =
67 | Elm.Kernel.Platform.batch
68 |
69 |
70 |
71 | -- FANCY STUFF
72 |
73 |
74 | {-| Transform the messages produced by a subscription.
75 | Very similar to [`Html.map`](/packages/elm-lang/html/latest/Html#map).
76 |
77 | This is very rarely useful in well-structured Elm code, so definitely read the
78 | sections on [reuse][] and [modularity][] in the guide before reaching for this!
79 |
80 | [reuse]: https://guide.elm-lang.org/reuse/
81 | [modularity]: https://guide.elm-lang.org/modularity/
82 | -}
83 | map : (a -> msg) -> Sub a -> Sub msg
84 | map =
85 | Elm.Kernel.Platform.map
86 |
--------------------------------------------------------------------------------
/src/Tuple.elm:
--------------------------------------------------------------------------------
1 | module Tuple exposing
2 | ( pair
3 | , first, second
4 | , mapFirst, mapSecond, mapBoth
5 | )
6 |
7 | {-| Elm has built-in syntax for tuples, so you can define 2D points like this:
8 |
9 | origin : (Float, Float)
10 | origin =
11 | (0, 0)
12 |
13 | position : (Float, Float)
14 | position =
15 | (3, 4)
16 |
17 | This module is a bunch of helpers for working with 2-tuples.
18 |
19 | **Note 1:** For more complex data, it is best to switch to records. So instead
20 | of representing a 3D point as `(3,4,5)` and not having any helper functions,
21 | represent it as `{ x = 3, y = 4, z = 5 }` and use all the built-in record
22 | syntax!
23 |
24 | **Note 2:** If your record contains a bunch of `Bool` and `Maybe` values,
25 | you may want to upgrade to union types. Check out [Joël’s post][ut] for more
26 | info on this. (Picking appropriate data structures is super important in Elm!)
27 |
28 | [ut]: https://robots.thoughtbot.com/modeling-with-union-types
29 |
30 | # Create
31 | @docs pair
32 |
33 | # Access
34 | @docs first, second
35 |
36 | # Map
37 | @docs mapFirst, mapSecond, mapBoth
38 |
39 | -}
40 |
41 |
42 |
43 | -- CREATE
44 |
45 |
46 | {-| Create a 2-tuple.
47 |
48 | -- pair 3 4 == (3, 4)
49 |
50 | zip : List a -> List b -> List (a, b)
51 | zip xs ys =
52 | List.map2 Tuple.pair xs ys
53 | -}
54 | pair : a -> b -> (a, b)
55 | pair a b =
56 | (a, b)
57 |
58 |
59 |
60 | -- ACCESS
61 |
62 |
63 | {-| Extract the first value from a tuple.
64 |
65 | first (3, 4) == 3
66 | first ("john", "doe") == "john"
67 | -}
68 | first : (a, b) -> a
69 | first (x,_) =
70 | x
71 |
72 |
73 | {-| Extract the second value from a tuple.
74 |
75 | second (3, 4) == 4
76 | second ("john", "doe") == "doe"
77 | -}
78 | second : (a, b) -> b
79 | second (_,y) =
80 | y
81 |
82 |
83 |
84 | -- MAP
85 |
86 |
87 | {-| Transform the first value in a tuple.
88 |
89 | import String
90 |
91 | mapFirst String.reverse ("stressed", 16) == ("desserts", 16)
92 | mapFirst String.length ("stressed", 16) == (8, 16)
93 | -}
94 | mapFirst : (a -> x) -> (a, b) -> (x, b)
95 | mapFirst func (x,y) =
96 | (func x, y)
97 |
98 |
99 | {-| Transform the second value in a tuple.
100 |
101 | mapSecond sqrt ("stressed", 16) == ("stressed", 4)
102 | mapSecond negate ("stressed", 16) == ("stressed", -16)
103 | -}
104 | mapSecond : (b -> y) -> (a, b) -> (a, y)
105 | mapSecond func (x,y) =
106 | (x, func y)
107 |
108 |
109 | {-| Transform both parts of a tuple.
110 |
111 | import String
112 |
113 | mapBoth String.reverse sqrt ("stressed", 16) == ("desserts", 4)
114 | mapBoth String.length negate ("stressed", 16) == (8, -16)
115 | -}
116 | mapBoth : (a -> x) -> (b -> y) -> (a, b) -> (x, y)
117 | mapBoth funcA funcB (x,y) =
118 | ( funcA x, funcB y )
119 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Error.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Error exposing (throw)
4 | import Elm.Kernel.Debug exposing (toString)
5 |
6 | */
7 |
8 |
9 | function _Error_throw__PROD(identifier)
10 | {
11 | throw new Error('https://github.com/elm-lang/core/blob/master/hints/' + identifier + '.md');
12 | }
13 |
14 |
15 | function _Error_throw__DEBUG(identifier, fact1, fact2, fact3, fact4)
16 | {
17 | switch(identifier)
18 | {
19 | case 0:
20 | throw new Error('Internal red-black tree invariant violated');
21 |
22 | case 1:
23 | var url = fact1;
24 | throw new Error('Cannot navigate to the following URL. It seems to be invalid:\n' + url);
25 |
26 | case 2:
27 | var message = fact1;
28 | throw new Error('Problem with the flags given to your Elm program on initialization.\n\n' + message);
29 |
30 | case 3:
31 | var portName = fact1;
32 | throw new Error('There can only be one port named `' + portName + '`, but your program has multiple.');
33 |
34 | case 4:
35 | var portName = fact1;
36 | var problem = fact2;
37 | throw new Error('Trying to send an unexpected type of value through port `' + portName + '`:\n' + problem);
38 |
39 | case 5:
40 | throw new Error('Trying to use `(==)` on functions.\nThere is no way to know if functions are "the same" in the Elm sense.\nRead more about this at https://package.elm-lang.org/packages/elm-lang/core/latest/Basics#== which describes why it is this way and what the better version will look like.');
41 |
42 | case 6:
43 | var moduleName = fact1;
44 | throw new Error('Your page is loading multiple Elm scripts with a module named ' + moduleName + '. Maybe a duplicate script is getting loaded accidentally? If not, rename one of them so I know which is which!');
45 |
46 | case 8:
47 | var moduleName = fact1;
48 | var region = fact2;
49 | var message = fact3;
50 | throw new Error('TODO in module `' + moduleName + '` ' + _Error_regionToString(region) + '\n\n' + message);
51 |
52 | case 9:
53 | var moduleName = fact1;
54 | var region = fact2;
55 | var value = fact3;
56 | var message = fact4;
57 | throw new Error(
58 | 'TODO in module `' + moduleName + '` from the `case` expression '
59 | + _Error_regionToString(region) + '\n\nIt received the following value:\n\n '
60 | + __Debug_toString(value).replace('\n', '\n ')
61 | + '\n\nBut the branch that handles it says:\n\n ' + message.replace('\n', '\n ')
62 | );
63 |
64 | case 10:
65 | throw new Error('Bug in https://github.com/elm-lang/virtual-dom/issues');
66 |
67 | case 11:
68 | throw new Error('Cannot perform mod 0. Division by zero error.');
69 | }
70 | }
71 |
72 | function _Error_regionToString(region)
73 | {
74 | if (region.__$start.__$line === region.__$end.__$line)
75 | {
76 | return 'on line ' + region.__$start.__$line;
77 | }
78 | return 'on lines ' + region.__$start.__$line + ' through ' + region.__$end.__$line;
79 | }
80 |
81 | function _Error_dictBug()
82 | {
83 | _Error_throw(0);
84 | }
85 |
--------------------------------------------------------------------------------
/tests/Test/CodeGen.elm:
--------------------------------------------------------------------------------
1 | module Test.CodeGen exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Test exposing (..)
5 | import Expect
6 | import Maybe
7 | import Maybe exposing (..)
8 |
9 |
10 | type Wrapper a
11 | = Wrapper a
12 |
13 |
14 | caseUnderscore : Maybe number -> number
15 | caseUnderscore m_ =
16 | case m_ of
17 | Just x ->
18 | x
19 |
20 | Nothing ->
21 | 0
22 |
23 |
24 | patternUnderscore : number
25 | patternUnderscore =
26 | case Just 42 of
27 | Just x_ ->
28 | x_
29 |
30 | Nothing ->
31 | 0
32 |
33 |
34 | letQualified : number
35 | letQualified =
36 | let
37 | (Wrapper x) =
38 | Wrapper 42
39 | in
40 | x
41 |
42 |
43 | caseQualified : number
44 | caseQualified =
45 | case Just 42 of
46 | Maybe.Just x ->
47 | x
48 |
49 | Nothing ->
50 | 0
51 |
52 |
53 | caseScope : String
54 | caseScope =
55 | case "Not this one!" of
56 | string ->
57 | case "Hi" of
58 | string ->
59 | string
60 |
61 |
62 | tests : Test
63 | tests =
64 | let
65 | -- We don't strictly speaking need annotations in this let-expression,
66 | -- but having these here exercises the parser to avoid regressions like
67 | -- https://github.com/elm-lang/elm-compiler/issues/1535
68 | underscores : Test
69 | underscores =
70 | describe "Underscores"
71 | [ test "case" <| \() -> Expect.equal 42 (caseUnderscore (Just 42))
72 | , test "pattern" <| \() -> Expect.equal 42 patternUnderscore
73 | ]
74 |
75 | qualifiedPatterns : Test
76 | qualifiedPatterns =
77 | describe "Qualified Patterns"
78 | [ test "let" <| \() -> Expect.equal 42 letQualified
79 | , test "case" <| \() -> Expect.equal 42 caseQualified
80 | ]
81 |
82 | scope : Test
83 | scope =
84 | describe "Scoping"
85 | [ test "case" <| \() -> Expect.equal "Hi" caseScope ]
86 |
87 | hex : Test
88 | hex =
89 | describe "Hex"
90 | [ test "0xFFFFFFFF" <|
91 | \() ->
92 | 0xFFFFFFFF
93 | |> Expect.equal 4294967295
94 | , test "0xD066F00D" <|
95 | \() ->
96 | 0xD066F00D
97 | |> Expect.equal 3496407053
98 | , test "0x00" <|
99 | \() ->
100 | 0x00
101 | |> Expect.equal 0
102 | ]
103 | in
104 | describe "CodeGen"
105 | [ underscores
106 | , qualifiedPatterns
107 | , scope
108 | , hex
109 | ]
110 |
--------------------------------------------------------------------------------
/tests/Test/Bitwise.elm:
--------------------------------------------------------------------------------
1 | module Test.Bitwise exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Bitwise
5 | import Test exposing (..)
6 | import Expect
7 |
8 |
9 | tests : Test
10 | tests =
11 | describe "Bitwise"
12 | [ describe "and"
13 | [ test "and with 32 bit integers" <| \() -> Expect.equal 1 (Bitwise.and 5 3)
14 | , test "and with 0 as first argument" <| \() -> Expect.equal 0 (Bitwise.and 0 1450)
15 | , test "and with 0 as second argument" <| \() -> Expect.equal 0 (Bitwise.and 274 0)
16 | , test "and with -1 as first argument" <| \() -> Expect.equal 2671 (Bitwise.and -1 2671)
17 | , test "and with -1 as second argument" <| \() -> Expect.equal 96 (Bitwise.and 96 -1)
18 | ]
19 | , describe "or"
20 | [ test "or with 32 bit integers" <| \() -> Expect.equal 15 (Bitwise.or 9 14)
21 | , test "or with 0 as first argument" <| \() -> Expect.equal 843 (Bitwise.or 0 843)
22 | , test "or with 0 as second argument" <| \() -> Expect.equal 19 (Bitwise.or 19 0)
23 | , test "or with -1 as first argument" <| \() -> Expect.equal -1 (Bitwise.or -1 2360)
24 | , test "or with -1 as second argument" <| \() -> Expect.equal -1 (Bitwise.or 3 -1)
25 | ]
26 | , describe "xor"
27 | [ test "xor with 32 bit integers" <| \() -> Expect.equal 604 (Bitwise.xor 580 24)
28 | , test "xor with 0 as first argument" <| \() -> Expect.equal 56 (Bitwise.xor 0 56)
29 | , test "xor with 0 as second argument" <| \() -> Expect.equal -268 (Bitwise.xor -268 0)
30 | , test "xor with -1 as first argument" <| \() -> Expect.equal -25 (Bitwise.xor -1 24)
31 | , test "xor with -1 as second argument" <| \() -> Expect.equal 25601 (Bitwise.xor -25602 -1)
32 | ]
33 | , describe "complement"
34 | [ test "complement a positive" <| \() -> Expect.equal -9 (Bitwise.complement 8)
35 | , test "complement a negative" <| \() -> Expect.equal 278 (Bitwise.complement -279)
36 | ]
37 | , describe "shiftLeftBy"
38 | [ test "8 |> shiftLeftBy 1 == 16" <| \() -> Expect.equal 16 (8 |> Bitwise.shiftLeftBy 1)
39 | , test "8 |> shiftLeftby 2 == 32" <| \() -> Expect.equal 32 (8 |> Bitwise.shiftLeftBy 2)
40 | ]
41 | , describe "shiftRightBy"
42 | [ test "32 |> shiftRight 1 == 16" <| \() -> Expect.equal 16 (32 |> Bitwise.shiftRightBy 1)
43 | , test "32 |> shiftRight 2 == 8" <| \() -> Expect.equal 8 (32 |> Bitwise.shiftRightBy 2)
44 | , test "-32 |> shiftRight 1 == -16" <| \() -> Expect.equal -16 (-32 |> Bitwise.shiftRightBy 1)
45 | ]
46 | , describe "shiftRightZfBy"
47 | [ test "32 |> shiftRightZfBy 1 == 16" <| \() -> Expect.equal 16 (32 |> Bitwise.shiftRightZfBy 1)
48 | , test "32 |> shiftRightZfBy 2 == 8" <| \() -> Expect.equal 8 (32 |> Bitwise.shiftRightZfBy 2)
49 | , test "-32 |> shiftRightZfBy 1 == 2147483632" <| \() -> Expect.equal 2147483632 (-32 |> Bitwise.shiftRightZfBy 1)
50 | ]
51 | ]
52 |
--------------------------------------------------------------------------------
/src/Debug.elm:
--------------------------------------------------------------------------------
1 | module Debug exposing
2 | ( toString
3 | , log
4 | , todo
5 | )
6 |
7 | {-| This module can be useful while _developing_ an application. It is not
8 | available for use in packages or production.
9 |
10 | # Debugging
11 | @docs toString, log, todo
12 | -}
13 |
14 |
15 | import Elm.Kernel.Debug
16 | import String exposing (String)
17 |
18 |
19 | {-| Turn any kind of value into a string.
20 |
21 | toString 42 == "42"
22 | toString [1,2] == "[1,2]"
23 | toString ('a', "cat", 13) == "('a', \"cat\", 13)"
24 | toString "he said, \"hi\"" == "\"he said, \\\"hi\\\"\""
25 |
26 | Notice that with strings, this is not the `identity` function. It escapes
27 | characters so if you say `Html.text (toString "he said, \"hi\"")` it will
28 | show `"he said, \"hi\""` rather than `he said, "hi"`. This makes it nice
29 | for viewing Elm data structures.
30 |
31 | **Note:** This is not available with `elm make --optimize` which gets rid of
32 | a bunch of runtime metadata. For example, it shortens record field names, and
33 | we need that info to `toString` the value! As a consequence, packages cannot
34 | use `toString` because they may be used in `--optimize` mode.
35 | -}
36 | toString : a -> String
37 | toString =
38 | Elm.Kernel.Debug.toString
39 |
40 |
41 | {-| Log a tagged value on the developer console, and then return the value.
42 |
43 | 1 + log "number" 1 -- equals 2, logs "number: 1"
44 | length (log "start" []) -- equals 0, logs "start: []"
45 |
46 | It is often possible to sprinkle this around to see if values are what you
47 | expect. It is kind of old-school to do it this way, but it works!
48 |
49 | **Note:** This is not available with `elm make --optimize` because (1) it
50 | relies on `toString` which has the same restriction and (2) it is not a pure
51 | function and would therefore have unpredictable behavior when paired with
52 | compiler optimizations that move code around.
53 |
54 | **Note:** If you want to create a terminal application that prints stuff out,
55 | use ports for now. That will give you full access to reading and writing in the
56 | terminal. We may have a package in Elm for this someday, but browser
57 | applications are the primary focus of platform development for now.
58 | -}
59 | log : String -> a -> a
60 | log =
61 | Elm.Kernel.Debug.log
62 |
63 |
64 | {-| This is a placeholder for code that you will write later.
65 |
66 | For example, if you are working with a large union type and have partially
67 | completed a case expression, it may make sense to do this:
68 |
69 | type Entity = Ship | Fish | Captain | Seagull
70 |
71 | drawEntity entity =
72 | case entity of
73 | Ship ->
74 | ...
75 |
76 | Fish ->
77 | ...
78 |
79 | _ ->
80 | Debug.todo "handle Captain and Seagull"
81 |
82 | The Elm compiler recognizes each `Debug.todo` so if you run into it, you get
83 | an **uncatchable runtime exception** that includes the module name and line
84 | number.
85 |
86 | **Note:** This is not available with `elm make --optimize` or packages. The
87 | idea is that a `todo` can be useful during development, but uncatchable runtime
88 | exceptions should not appear in the resulting applications.
89 |
90 | **Note:** For the equivalent of try/catch error handling in Elm, use modules
91 | like [`Maybe`](#Maybe) and [`Result`](#Result) which guarantee that no error
92 | goes unhandled!
93 | -}
94 | todo : String -> a
95 | todo =
96 | Elm.Kernel.Debug.todo
97 |
98 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/JsArray.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Utils exposing (Tuple2)
4 |
5 | */
6 |
7 |
8 | var _JsArray_empty = [];
9 |
10 | function _JsArray_singleton(value)
11 | {
12 | return [value];
13 | }
14 |
15 | function _JsArray_length(array)
16 | {
17 | return array.length;
18 | }
19 |
20 | var _JsArray_initialize = F3(function(size, offset, func)
21 | {
22 | var result = new Array(size);
23 |
24 | for (var i = 0; i < size; i++)
25 | {
26 | result[i] = func(offset + i);
27 | }
28 |
29 | return result;
30 | });
31 |
32 | var _JsArray_initializeFromList = F2(function (max, ls)
33 | {
34 | var result = new Array(max);
35 |
36 | for (var i = 0; i < max && ls.b; i++)
37 | {
38 | result[i] = ls.a;
39 | ls = ls.b;
40 | }
41 |
42 | result.length = i;
43 | return __Utils_Tuple2(result, ls);
44 | });
45 |
46 | var _JsArray_unsafeGet = F2(function(index, array)
47 | {
48 | return array[index];
49 | });
50 |
51 | var _JsArray_unsafeSet = F3(function(index, value, array)
52 | {
53 | var length = array.length;
54 | var result = new Array(length);
55 |
56 | for (var i = 0; i < length; i++)
57 | {
58 | result[i] = array[i];
59 | }
60 |
61 | result[index] = value;
62 | return result;
63 | });
64 |
65 | var _JsArray_push = F2(function(value, array)
66 | {
67 | var length = array.length;
68 | var result = new Array(length + 1);
69 |
70 | for (var i = 0; i < length; i++)
71 | {
72 | result[i] = array[i];
73 | }
74 |
75 | result[length] = value;
76 | return result;
77 | });
78 |
79 | var _JsArray_foldl = F3(function(func, acc, array)
80 | {
81 | var length = array.length;
82 |
83 | for (var i = 0; i < length; i++)
84 | {
85 | acc = A2(func, array[i], acc);
86 | }
87 |
88 | return acc;
89 | });
90 |
91 | var _JsArray_foldr = F3(function(func, acc, array)
92 | {
93 | for (var i = array.length - 1; i >= 0; i--)
94 | {
95 | acc = A2(func, array[i], acc);
96 | }
97 |
98 | return acc;
99 | });
100 |
101 | var _JsArray_map = F2(function(func, array)
102 | {
103 | var length = array.length;
104 | var result = new Array(length);
105 |
106 | for (var i = 0; i < length; i++)
107 | {
108 | result[i] = func(array[i]);
109 | }
110 |
111 | return result;
112 | });
113 |
114 | var _JsArray_indexedMap = F3(function(func, offset, array)
115 | {
116 | var length = array.length;
117 | var result = new Array(length);
118 |
119 | for (var i = 0; i < length; i++)
120 | {
121 | result[i] = A2(func, offset + i, array[i]);
122 | }
123 |
124 | return result;
125 | });
126 |
127 | var _JsArray_slice = F3(function(from, to, array)
128 | {
129 | return array.slice(from, to);
130 | });
131 |
132 | var _JsArray_appendN = F3(function(n, dest, source)
133 | {
134 | var destLen = dest.length;
135 | var itemsToCopy = n - destLen;
136 |
137 | if (itemsToCopy > source.length)
138 | {
139 | itemsToCopy = source.length;
140 | }
141 |
142 | var size = destLen + itemsToCopy;
143 | var result = new Array(size);
144 |
145 | for (var i = 0; i < destLen; i++)
146 | {
147 | result[i] = dest[i];
148 | }
149 |
150 | for (var i = 0; i < itemsToCopy; i++)
151 | {
152 | result[i + destLen] = source[i];
153 | }
154 |
155 | return result;
156 | });
157 |
--------------------------------------------------------------------------------
/src/Platform.elm:
--------------------------------------------------------------------------------
1 | module Platform exposing
2 | ( Program, worker
3 | , Task, ProcessId
4 | , Router, sendToApp, sendToSelf
5 | )
6 |
7 | {-|
8 |
9 | # Programs
10 | @docs Program, worker
11 |
12 | # Platform Internals
13 |
14 | ## Tasks and Processes
15 | @docs Task, ProcessId
16 |
17 | ## Effect Manager Helpers
18 |
19 | An extremely tiny portion of library authors should ever write effect managers.
20 | Fundamentally, Elm needs maybe 10 of them total. I get that people are smart,
21 | curious, etc. but that is not a substitute for a legitimate reason to make an
22 | effect manager. Do you have an *organic need* this fills? Or are you just
23 | curious? Public discussions of your explorations should be framed accordingly.
24 |
25 | @docs Router, sendToApp, sendToSelf
26 | -}
27 |
28 | import Basics exposing (Never)
29 | import Elm.Kernel.Platform
30 | import Elm.Kernel.Scheduler
31 | import Platform.Cmd exposing (Cmd)
32 | import Platform.Sub exposing (Sub)
33 |
34 |
35 |
36 | -- PROGRAMS
37 |
38 |
39 | {-| A `Program` describes an Elm program! How does it react to input? Does it
40 | show anything on screen? Etc.
41 | -}
42 | type Program flags model msg = Program
43 |
44 |
45 | {-| Create a [headless][] program with no user interface.
46 |
47 | This is great if you want to use Elm as the “brain” for something
48 | else. For example, you could send messages out ports to modify the DOM, but do
49 | all the complex logic in Elm.
50 |
51 | [headless]: https://en.wikipedia.org/wiki/Headless_software
52 |
53 | Initializing a headless program from JavaScript looks like this:
54 |
55 | ```javascript
56 | var app = Elm.MyThing.worker();
57 | ```
58 |
59 | If _do_ want to control the user interface in Elm, the [`Browser`][browser]
60 | module has a few ways to create that kind of `Program` instead!
61 |
62 | [headless]: https://en.wikipedia.org/wiki/Headless_software
63 | [browser]: /packages/elm-lang/browser/latest/Browser
64 | -}
65 | worker
66 | : { init : flags -> ( model, Cmd msg )
67 | , update : msg -> model -> ( model, Cmd msg )
68 | , subscriptions : model -> Sub msg
69 | }
70 | -> Program flags model msg
71 | worker =
72 | Elm.Kernel.Platform.worker
73 |
74 |
75 |
76 | -- TASKS and PROCESSES
77 |
78 |
79 | {-| Head over to the documentation for the [`Task`](Task) module for more
80 | information on this. It is only defined here because it is a platform
81 | primitive.
82 | -}
83 | type Task err ok = Task
84 |
85 |
86 | {-| Head over to the documentation for the [`Process`](Process) module for
87 | information on this. It is only defined here because it is a platform
88 | primitive.
89 | -}
90 | type ProcessId = ProcessId
91 |
92 |
93 |
94 | -- EFFECT MANAGER INTERNALS
95 |
96 |
97 | {-| An effect manager has access to a “router” that routes messages between
98 | the main app and your individual effect manager.
99 | -}
100 | type Router appMsg selfMsg =
101 | Router
102 |
103 |
104 | {-| Send the router a message for the main loop of your app. This message will
105 | be handled by the overall `update` function, just like events from `Html`.
106 | -}
107 | sendToApp : Router msg a -> msg -> Task x ()
108 | sendToApp =
109 | Elm.Kernel.Platform.sendToApp
110 |
111 |
112 | {-| Send the router a message for your effect manager. This message will
113 | be routed to the `onSelfMsg` function, where you can update the state of your
114 | effect manager as necessary.
115 |
116 | As an example, the effect manager for web sockets
117 | -}
118 | sendToSelf : Router a msg -> msg -> Task x ()
119 | sendToSelf =
120 | Elm.Kernel.Platform.sendToSelf
121 |
--------------------------------------------------------------------------------
/src/Process.elm:
--------------------------------------------------------------------------------
1 | module Process exposing
2 | ( Id
3 | , spawn
4 | , sleep
5 | , kill
6 | )
7 |
8 | {-|
9 |
10 | # Processes
11 | @docs Id, spawn, sleep, kill
12 |
13 | ## Future Plans
14 |
15 | Right now, this library is pretty sparse. For example, there is no public API
16 | for processes to communicate with each other. This is a really important
17 | ability, but it is also something that is extraordinarily easy to get wrong!
18 |
19 | I think the trend will be towards an Erlang style of concurrency, where every
20 | process has an “event queue” that anyone can send messages to. I currently
21 | think the API will be extended to be more like this:
22 |
23 | type Id exit msg
24 |
25 | spawn : Task exit a -> Task x (Id exit Never)
26 |
27 | kill : Id exit msg -> Task x ()
28 |
29 | send : Id exit msg -> msg -> Task x ()
30 |
31 | A process `Id` will have two type variables to make sure all communication is
32 | valid. The `exit` type describes the messages that are produced if the process
33 | fails because of user code. So if processes are linked and trapping errors,
34 | they will need to handle this. The `msg` type just describes what kind of
35 | messages this process can be sent by strangers.
36 |
37 | We shall see though! This is just a draft that does not cover nearly everything
38 | it needs to, so the long-term vision for concurrency in Elm will be rolling out
39 | slowly as I get more data and experience.
40 |
41 | I ask that people bullish on compiling to node.js keep this in mind. I think we
42 | can do better than the hopelessly bad concurrency model of node.js, and I hope
43 | the Elm community will be supportive of being more ambitious, even if it takes
44 | longer. That’s kind of what Elm is all about.
45 | -}
46 |
47 | import Basics exposing (Float, Never)
48 | import Elm.Kernel.Scheduler
49 | import Elm.Kernel.Process
50 | import Platform
51 | import Task exposing (Task)
52 |
53 |
54 | {-| A light-weight process that runs concurrently. You can use `spawn` to
55 | get a bunch of different tasks running in different processes. The Elm runtime
56 | will interleave their progress. So if a task is taking too long, we will pause
57 | it at an `andThen` and switch over to other stuff.
58 |
59 | **Note:** We make a distinction between *concurrency* which means interleaving
60 | different sequences and *parallelism* which means running different
61 | sequences at the exact same time. For example, a
62 | [time-sharing system](https://en.wikipedia.org/wiki/Time-sharing) is definitely
63 | concurrent, but not necessarily parallel. So even though JS runs within a
64 | single OS-level thread, Elm can still run things concurrently.
65 | -}
66 | type alias Id =
67 | Platform.ProcessId
68 |
69 |
70 | {-| Run a task in its own light-weight process. In the following example,
71 | `task1` and `task2` will be interleaved. If `task1` makes a long HTTP request
72 | or is just taking a long time, we can hop over to `task2` and do some work
73 | there.
74 |
75 | spawn task1
76 | |> Task.andThen (\_ -> spawn task2)
77 |
78 | **Note:** This creates a relatively restricted kind of `Process` because it
79 | cannot receive any messages. More flexibility for user-defined processes will
80 | come in a later release!
81 | -}
82 | spawn : Task x a -> Task y Id
83 | spawn =
84 | Elm.Kernel.Scheduler.spawn
85 |
86 |
87 | {-| Block progress on the current process for the given number of milliseconds.
88 | The JavaScript equivalent of this is [`setTimeout`][setTimeout] which lets you
89 | delay work until later.
90 |
91 | [setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
92 | -}
93 | sleep : Float -> Task x ()
94 | sleep =
95 | Elm.Kernel.Process.sleep
96 |
97 |
98 | {-| Sometimes you `spawn` a process, but later decide it would be a waste to
99 | have it keep running and doing stuff. The `kill` function will force a process
100 | to bail on whatever task it is running. So if there is an HTTP request in
101 | flight, it will also abort the request.
102 | -}
103 | kill : Id -> Task x ()
104 | kill =
105 | Elm.Kernel.Scheduler.kill
106 |
107 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Scheduler.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Utils exposing (Tuple0)
4 |
5 | */
6 |
7 |
8 | // TASKS
9 |
10 | function _Scheduler_succeed(value)
11 | {
12 | return {
13 | $: __1_SUCCEED,
14 | __value: value
15 | };
16 | }
17 |
18 | function _Scheduler_fail(error)
19 | {
20 | return {
21 | $: __1_FAIL,
22 | __value: error
23 | };
24 | }
25 |
26 | function _Scheduler_binding(callback)
27 | {
28 | return {
29 | $: __1_BINDING,
30 | __callback: callback,
31 | __kill: null
32 | };
33 | }
34 |
35 | var _Scheduler_andThen = F2(function(callback, task)
36 | {
37 | return {
38 | $: __1_AND_THEN,
39 | __callback: callback,
40 | __task: task
41 | };
42 | });
43 |
44 | var _Scheduler_onError = F2(function(callback, task)
45 | {
46 | return {
47 | $: __1_ON_ERROR,
48 | __callback: callback,
49 | __task: task
50 | };
51 | });
52 |
53 | function _Scheduler_receive(callback)
54 | {
55 | return {
56 | $: __1_RECEIVE,
57 | __callback: callback
58 | };
59 | }
60 |
61 |
62 | // PROCESSES
63 |
64 | var _Scheduler_guid = 0;
65 |
66 | function _Scheduler_rawSpawn(task)
67 | {
68 | var proc = {
69 | $: __2_PROCESS,
70 | __id: _Scheduler_guid++,
71 | __root: task,
72 | __stack: null,
73 | __mailbox: []
74 | };
75 |
76 | _Scheduler_enqueue(proc);
77 |
78 | return proc;
79 | }
80 |
81 | function _Scheduler_spawn(task)
82 | {
83 | return _Scheduler_binding(function(callback) {
84 | callback(_Scheduler_succeed(_Scheduler_rawSpawn(task)));
85 | });
86 | }
87 |
88 | function _Scheduler_rawSend(proc, msg)
89 | {
90 | proc.__mailbox.push(msg);
91 | _Scheduler_enqueue(proc);
92 | }
93 |
94 | var _Scheduler_send = F2(function(proc, msg)
95 | {
96 | return _Scheduler_binding(function(callback) {
97 | _Scheduler_rawSend(proc, msg);
98 | callback(_Scheduler_succeed(__Utils_Tuple0));
99 | });
100 | });
101 |
102 | function _Scheduler_kill(proc)
103 | {
104 | return _Scheduler_binding(function(callback) {
105 | var task = proc.__root;
106 | if (task.$ === __1_BINDING && task.__kill)
107 | {
108 | task.__kill();
109 | }
110 |
111 | proc.__root = null;
112 |
113 | callback(_Scheduler_succeed(__Utils_Tuple0));
114 | });
115 | }
116 |
117 |
118 | /* STEP PROCESSES
119 |
120 | type alias Process =
121 | { $ : tag
122 | , id : unique_id
123 | , root : Task
124 | , stack : null | { $: SUCCEED | FAIL, a: callback, b: stack }
125 | , mailbox : [msg]
126 | }
127 |
128 | */
129 |
130 |
131 | var _Scheduler_working = false;
132 | var _Scheduler_queue = [];
133 |
134 |
135 | function _Scheduler_enqueue(proc)
136 | {
137 | _Scheduler_queue.push(proc);
138 | if (_Scheduler_working)
139 | {
140 | return;
141 | }
142 | _Scheduler_working = true;
143 | while (proc = _Scheduler_queue.shift())
144 | {
145 | _Scheduler_step(proc);
146 | }
147 | _Scheduler_working = false;
148 | }
149 |
150 |
151 | function _Scheduler_step(proc)
152 | {
153 | while (proc.__root)
154 | {
155 | var rootTag = proc.__root.$;
156 | if (rootTag === __1_SUCCEED || rootTag === __1_FAIL)
157 | {
158 | while (proc.__stack && proc.__stack.$ !== rootTag)
159 | {
160 | proc.__stack = proc.__stack.__rest;
161 | }
162 | if (!proc.__stack)
163 | {
164 | return;
165 | }
166 | proc.__root = proc.__stack.__callback(proc.__root.__value);
167 | proc.__stack = proc.__stack.__rest;
168 | }
169 | else if (rootTag === __1_BINDING)
170 | {
171 | proc.__root.__kill = proc.__root.__callback(function(newRoot) {
172 | proc.__root = newRoot;
173 | _Scheduler_enqueue(proc);
174 | });
175 | return;
176 | }
177 | else if (rootTag === __1_RECEIVE)
178 | {
179 | if (proc.__mailbox.length === 0)
180 | {
181 | return;
182 | }
183 | proc.__root = proc.__root.__callback(proc.__mailbox.shift());
184 | }
185 | else // if (rootTag === __1_AND_THEN || rootTag === __1_ON_ERROR)
186 | {
187 | proc.__stack = {
188 | $: rootTag === __1_AND_THEN ? __1_SUCCEED : __1_FAIL,
189 | __callback: proc.__root.__callback,
190 | __rest: proc.__stack
191 | };
192 | proc.__root = proc.__root.__task;
193 | }
194 | }
195 | }
196 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Utils.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Array exposing (toList)
4 | import Basics exposing (LT, EQ, GT)
5 | import Dict exposing (toList)
6 | import Elm.Kernel.Error exposing (throw)
7 | import Elm.Kernel.List exposing (Cons, Nil)
8 | import Set exposing (toList)
9 |
10 | */
11 |
12 |
13 | // EQUALITY
14 |
15 | function _Utils_eq(x, y)
16 | {
17 | for (
18 | var pair, stack = [], isEqual = _Utils_eqHelp(x, y, 0, stack);
19 | isEqual && (pair = stack.pop());
20 | isEqual = _Utils_eqHelp(pair.a, pair.b, 0, stack)
21 | )
22 | {}
23 |
24 | return isEqual;
25 | }
26 |
27 | function _Utils_eqHelp(x, y, depth, stack)
28 | {
29 | if (depth > 100)
30 | {
31 | stack.push(_Utils_Tuple2(x,y));
32 | return true;
33 | }
34 |
35 | if (x === y)
36 | {
37 | return true;
38 | }
39 |
40 | if (typeof x !== 'object' || x === null || y === null)
41 | {
42 | typeof x === 'function' && __Error_throw(5);
43 | return false;
44 | }
45 |
46 | /**__DEBUG/
47 | if (x.$ === 'Set_elm_builtin')
48 | {
49 | x = __Set_toList(x);
50 | y = __Set_toList(y);
51 | }
52 | if (x.$ === 'RBNode_elm_builtin' || x.$ === 'RBEmpty_elm_builtin')
53 | {
54 | x = __Dict_toList(x);
55 | y = __Dict_toList(y);
56 | }
57 | //*/
58 |
59 | /**__PROD/
60 | if (x.$ < 0)
61 | {
62 | x = __Dict_toList(x);
63 | y = __Dict_toList(y);
64 | }
65 | //*/
66 |
67 | for (var key in x)
68 | {
69 | if (!_Utils_eqHelp(x[key], y[key], depth + 1, stack))
70 | {
71 | return false;
72 | }
73 | }
74 | return true;
75 | }
76 |
77 | var _Utils_equal = F2(_Utils_eq);
78 | var _Utils_notEqual = F2(function(a, b) { return !_Utils_eq(a,b); });
79 |
80 |
81 |
82 | // COMPARISONS
83 |
84 | // Code in Generate/JavaScript.hs, Basics.js, and List.js depends on
85 | // the particular integer values assigned to LT, EQ, and GT.
86 |
87 | function _Utils_cmp(x, y, ord)
88 | {
89 | if (typeof x !== 'object')
90 | {
91 | return x === y ? /*EQ*/ 0 : x < y ? /*LT*/ -1 : /*GT*/ 1;
92 | }
93 |
94 | /**__DEBUG/
95 | if (x instanceof String)
96 | {
97 | var a = x.valueOf();
98 | var b = y.valueOf();
99 | return a === b ? 0 : a < b ? -1 : 1;
100 | }
101 | //*/
102 |
103 | /**__PROD/
104 | if (!x.$)
105 | //*/
106 | /**__DEBUG/
107 | if (x.$[0] === '#')
108 | //*/
109 | {
110 | return (ord = _Utils_cmp(x.a, y.a))
111 | ? ord
112 | : (ord = _Utils_cmp(x.b, y.b))
113 | ? ord
114 | : _Utils_cmp(x.c, y.c);
115 | }
116 |
117 | // traverse conses until end of a list or a mismatch
118 | for (; x.b && y.b && !(ord = _Utils_cmp(x.a, y.a)); x = x.b, y = y.b) {} // WHILE_CONSES
119 | return ord || (x.b ? /*GT*/ 1 : y.b ? /*LT*/ -1 : /*EQ*/ 0);
120 | }
121 |
122 | var _Utils_lt = F2(function(a, b) { return _Utils_cmp(a, b) < 0; });
123 | var _Utils_le = F2(function(a, b) { return _Utils_cmp(a, b) < 1; });
124 | var _Utils_gt = F2(function(a, b) { return _Utils_cmp(a, b) > 0; });
125 | var _Utils_ge = F2(function(a, b) { return _Utils_cmp(a, b) >= 0; });
126 |
127 | var _Utils_compare = F2(function(x, y)
128 | {
129 | var n = _Utils_cmp(x, y);
130 | return n < 0 ? __Basics_LT : n ? __Basics_GT : __Basics_EQ;
131 | });
132 |
133 |
134 | // COMMON VALUES
135 |
136 | var _Utils_Tuple0__PROD = 0;
137 | var _Utils_Tuple0__DEBUG = { $: '#0' };
138 |
139 | function _Utils_Tuple2__PROD(a, b) { return { a: a, b: b }; }
140 | function _Utils_Tuple2__DEBUG(a, b) { return { $: '#2', a: a, b: b }; }
141 |
142 | function _Utils_Tuple3__PROD(a, b, c) { return { a: a, b: b, c: c }; }
143 | function _Utils_Tuple3__DEBUG(a, b, c) { return { $: '#3', a: a, b: b, c: c }; }
144 |
145 | function _Utils_chr__PROD(c) { return c; }
146 | function _Utils_chr__DEBUG(c) { return new String(c); }
147 |
148 |
149 | // RECORDS
150 |
151 | function _Utils_update(oldRecord, updatedFields)
152 | {
153 | var newRecord = {};
154 |
155 | for (var key in oldRecord)
156 | {
157 | newRecord[key] = oldRecord[key];
158 | }
159 |
160 | for (var key in updatedFields)
161 | {
162 | newRecord[key] = updatedFields[key];
163 | }
164 |
165 | return newRecord;
166 | }
167 |
168 |
169 | // APPEND
170 |
171 | var _Utils_append = F2(_Utils_ap);
172 |
173 | function _Utils_ap(xs, ys)
174 | {
175 | // append Strings
176 | if (typeof xs === 'string')
177 | {
178 | return xs + ys;
179 | }
180 |
181 | // append Lists
182 | if (!xs.b)
183 | {
184 | return ys;
185 | }
186 | var root = __List_Cons(xs.a, ys);
187 | xs = xs.b
188 | for (var curr = root; xs.b; xs = xs.b) // WHILE_CONS
189 | {
190 | curr = curr.b = __List_Cons(xs.a, ys);
191 | }
192 | return root;
193 | }
194 |
--------------------------------------------------------------------------------
/changelog.md:
--------------------------------------------------------------------------------
1 | # 0.15
2 |
3 | ### Syntax
4 |
5 | New `import` syntax with keyword `exposing`.
6 |
7 | ### Module Changes
8 |
9 | * Move `Http` to `elm-http` package and totally redo API
10 | * Remove `WebSocket` module
11 | * Add `Task` module
12 |
13 | ### Channels become Mailboxes
14 |
15 | `Graphics.Input` now works with this API (from module `Signal`):
16 |
17 | ```elm
18 | type alias Mailbox a = { address : Address a, signal : Signal a }
19 |
20 | mailbox : a -> Mailbox a
21 | ```
22 |
23 | You can then send messages to the `Address` with functions like `Signal.send`
24 | and `Signal.message`, or create forwarding addresses with `Signal.forwardTo`.
25 |
26 | ### Text in Collages
27 |
28 | `Graphics.Collage` now has two new functions:
29 |
30 | ```elm
31 | text : Text -> Form
32 | outlinedText : LineStyle -> Text -> Form
33 | ```
34 |
35 | These functions render text with the canvas, making things quite a bit faster.
36 | The underlying implementation of `Text` has also been improved dramatically.
37 |
38 | ### Miscellaneous
39 |
40 | * Change types of `head`, `tail`, `maximum`, `minimum` by wrapping output in `Maybe`
41 | * Move `leftAligned`, `centered`, `rightAligned` from `Text` to `Graphics.Element`
42 | * Move `asText` from `Text` to `Graphics.Element`, renaming it to `show` in the process
43 | * Remove `Text.plainText` (can be replaced by `Graphics.Element.leftAligned << Text.fromString`)
44 | * Change type of `Keyboard.keysDown` from `Signal (List KeyCode)` to `Signal (Set KeyCode)`
45 | * Remove `Keyboard.directions`
46 | * Rename `Keyboard.lastPressed` to `Keyboard.presses`
47 |
48 |
49 | # 0.14
50 |
51 | ### Syntax
52 |
53 | * Keyword `type` becomes `type alias`
54 | * Keyword `data` becomes `type`
55 | * Remove special list syntax in types, so `[a]` becomes `List a`
56 |
57 |
58 | ### Reduce Default Imports
59 |
60 | The set of default imports has been reduced to the following:
61 |
62 | ```haskell
63 | import Basics (..)
64 | import Maybe ( Maybe( Just, Nothing ) )
65 | import Result ( Result( Ok, Err ) )
66 | import List ( List )
67 | import Signal ( Signal )
68 | ```
69 |
70 | ### Make JSON parsing easy
71 |
72 | * Added `Json.Decode` and `Json.Encode` libraries
73 |
74 |
75 | ### Use more natural names
76 |
77 | * Rename `String.show` to `String.toString`
78 |
79 | * Replace `List.zip` with `List.map2 (,)`
80 | * Replace `List.zipWith f` with `List.map2 f`
81 |
82 | * Rename `Signal.liftN` to `Signal.mapN`
83 | * Rename `Signal.merges` to `Signal.mergeMany`
84 |
85 |
86 | ### Simplify Signal Library
87 |
88 | * Revamp `Input` concept as `Signal.Channel`
89 | * Remove `Signal.count`
90 | * Remove `Signal.countIf`
91 | * Remove `Signal.combine`
92 |
93 |
94 | ### Randomness Done Right
95 |
96 | * No longer signal-based
97 | * Use a `Generator` to create random values
98 |
99 |
100 |
101 | ### Revamp Maybes and Error Handling
102 |
103 | * Add the following functions to `Maybe`
104 |
105 | withDefault : a -> Maybe a -> a
106 | oneOf : List (Maybe a) -> Maybe a
107 | map : (a -> b) -> Maybe a -> Maybe b
108 | andThen : Maybe a -> (a -> Maybe b) -> Maybe b
109 |
110 | * Remove `Maybe.maybe` so `maybe 0 sqrt Nothing` becomes `withDefault 0 (map sqrt Nothing)`
111 |
112 | * Remove `Maybe.isJust` and `Maybe.isNothing` in favor of pattern matching
113 |
114 | * Add `Result` library for proper error handling. This is for cases when
115 | you want a computation to succeed, but if there is a mistake, it should
116 | produce a nice error message.
117 |
118 | * Remove `Either` in favor of `Result` or custom union types
119 |
120 | * Revamp functions that result in a `Maybe`.
121 |
122 | - Remove `Dict.getOrElse` and `Dict.getOrFail` in favor of `withDefault 0 (Dict.get key dict)`
123 | - Remove `Array.getOrElse` and `Array.getOrFail` in favor of `withDefault 0 (Array.get index array)`
124 | - Change `String.toInt : String -> Maybe Int` to `String.toInt : String -> Result String Int`
125 | - Change `String.toFloat : String -> Maybe Float` to `String.toFloat : String -> Result String Float`
126 |
127 |
128 | ### Make appending more logical
129 |
130 | * Add the following functions to `Text`:
131 |
132 | empty : Text
133 | append : Text -> Text -> Text
134 | concat : [Text] -> Text
135 | join : Text -> [Text] -> Text
136 |
137 | * Make the following changes in `List`:
138 | - Replace `(++)` with `append`
139 | - Remove `join`
140 |
141 | ### Miscellaneous
142 |
143 | * Rename `Text.toText` to `Text.fromString`
144 |
--------------------------------------------------------------------------------
/tests/Test/Char.elm:
--------------------------------------------------------------------------------
1 | module Test.Char exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Char exposing (..)
5 | import List
6 | import Test exposing (..)
7 | import Expect
8 |
9 |
10 | lower =
11 | [ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z' ]
12 |
13 |
14 | upper =
15 | [ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z' ]
16 |
17 |
18 | dec =
19 | [ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]
20 |
21 |
22 | oct =
23 | List.take 8 dec
24 |
25 |
26 | hexLower =
27 | List.take 6 lower
28 |
29 |
30 | hexUpper =
31 | List.take 6 upper
32 |
33 |
34 | hex =
35 | List.append hexLower hexUpper |> List.append dec
36 |
37 |
38 | lowerCodes =
39 | List.range 97 (97 + List.length lower - 1)
40 |
41 |
42 | upperCodes =
43 | List.range 65 (65 + List.length upper - 1)
44 |
45 |
46 | decCodes =
47 | List.range 48 (48 + List.length dec - 1)
48 |
49 |
50 | oneOf : List a -> a -> Bool
51 | oneOf =
52 | flip List.member
53 |
54 |
55 | tests : Test
56 | tests =
57 | describe "Char"
58 | [ describe "toCode"
59 | [ test "a-z" <| \() -> Expect.equal (lowerCodes) (List.map toCode lower)
60 | , test "A-Z" <| \() -> Expect.equal (upperCodes) (List.map toCode upper)
61 | , test "0-9" <| \() -> Expect.equal (decCodes) (List.map toCode dec)
62 | , test "UTF-16" <| \() -> Expect.equal 0x1D306 (Char.toCode '𝌆')
63 | ]
64 | , describe "fromCode"
65 | [ test "a-z" <| \() -> Expect.equal (lower) (List.map fromCode lowerCodes)
66 | , test "A-Z" <| \() -> Expect.equal (upper) (List.map fromCode upperCodes)
67 | , test "0-9" <| \() -> Expect.equal (dec) (List.map fromCode decCodes)
68 | , test "UTF-16" <| \() -> Expect.equal '𝌆' (Char.fromCode 0x1D306)
69 | ]
70 | , describe "toLocaleLower"
71 | [ test "a-z" <| \() -> Expect.equal (lower) (List.map toLocaleLower lower)
72 | , test "A-Z" <| \() -> Expect.equal (lower) (List.map toLocaleLower upper)
73 | , test "0-9" <| \() -> Expect.equal (dec) (List.map toLocaleLower dec)
74 | ]
75 | , describe "toLocaleUpper"
76 | [ test "a-z" <| \() -> Expect.equal (upper) (List.map toLocaleUpper lower)
77 | , test "A-Z" <| \() -> Expect.equal (upper) (List.map toLocaleUpper upper)
78 | , test "0-9" <| \() -> Expect.equal (dec) (List.map toLocaleUpper dec)
79 | ]
80 | , describe "toLower"
81 | [ test "a-z" <| \() -> Expect.equal (lower) (List.map toLower lower)
82 | , test "A-Z" <| \() -> Expect.equal (lower) (List.map toLower upper)
83 | , test "0-9" <| \() -> Expect.equal (dec) (List.map toLower dec)
84 | ]
85 | , describe "toUpper"
86 | [ test "a-z" <| \() -> Expect.equal (upper) (List.map toUpper lower)
87 | , test "A-Z" <| \() -> Expect.equal (upper) (List.map toUpper upper)
88 | , test "0-9" <| \() -> Expect.equal (dec) (List.map toUpper dec)
89 | ]
90 | , describe "isLower"
91 | [ test "a-z" <| \() -> Expect.equal (True) (List.all isLower lower)
92 | , test "A-Z" <| \() -> Expect.equal (False) (List.any isLower upper)
93 | , test "0-9" <| \() -> Expect.equal (False) (List.any isLower dec)
94 | ]
95 | , describe "isUpper"
96 | [ test "a-z" <| \() -> Expect.equal (False) (List.any isUpper lower)
97 | , test "A-Z" <| \() -> Expect.equal (True) (List.all isUpper upper)
98 | , test "0-9" <| \() -> Expect.equal (False) (List.any isUpper dec)
99 | ]
100 | , describe "isDigit"
101 | [ test "a-z" <| \() -> Expect.equal (False) (List.any isDigit lower)
102 | , test "A-Z" <| \() -> Expect.equal (False) (List.any isDigit upper)
103 | , test "0-9" <| \() -> Expect.equal (True) (List.all isDigit dec)
104 | ]
105 | , describe "isHexDigit"
106 | [ test "a-z" <| \() -> Expect.equal (List.map (oneOf hex) lower) (List.map isHexDigit lower)
107 | , test "A-Z" <| \() -> Expect.equal (List.map (oneOf hex) upper) (List.map isHexDigit upper)
108 | , test "0-9" <| \() -> Expect.equal (True) (List.all isHexDigit dec)
109 | ]
110 | , describe "isOctDigit"
111 | [ test "a-z" <| \() -> Expect.equal (False) (List.any isOctDigit lower)
112 | , test "A-Z" <| \() -> Expect.equal (False) (List.any isOctDigit upper)
113 | , test "0-9" <| \() -> Expect.equal (List.map (oneOf oct) dec) (List.map isOctDigit dec)
114 | ]
115 | ]
116 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Debug.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Array exposing (toList)
4 | import Dict exposing (toList)
5 | import Elm.Kernel.Error exposing (throw)
6 | import Set exposing (toList)
7 |
8 | */
9 |
10 |
11 | // LOG
12 |
13 | var _Debug_log__PROD = F2(function(tag, value)
14 | {
15 | return value;
16 | });
17 |
18 | var _Debug_log__DEBUG = F2(function(tag, value)
19 | {
20 | console.log(tag + ': ' + _Debug_toString(value));
21 | return value;
22 | });
23 |
24 |
25 | // TODOS
26 |
27 | function _Debug_todo(moduleName, region)
28 | {
29 | return function(message) {
30 | __Error_throw(8, moduleName, region, message);
31 | };
32 | }
33 |
34 | function _Debug_todoCase(moduleName, region, value)
35 | {
36 | return function(message) {
37 | __Error_throw(9, moduleName, region, value, message);
38 | };
39 | }
40 |
41 |
42 | // TO STRING
43 |
44 | function _Debug_toString__PROD(value)
45 | {
46 | return '';
47 | }
48 |
49 | function _Debug_toString__DEBUG(value)
50 | {
51 | return _Debug_toAnsiString(false, value);
52 | }
53 |
54 | function _Debug_toAnsiString(ansi, value)
55 | {
56 | if (typeof value === 'function')
57 | {
58 | return _Debug_internalColor(ansi, '');
59 | }
60 |
61 | if (typeof value === 'boolean')
62 | {
63 | return _Debug_ctorColor(ansi, value ? 'True' : 'False');
64 | }
65 |
66 | if (typeof value === 'number')
67 | {
68 | return _Debug_numberColor(ansi, value + '');
69 | }
70 |
71 | if (value instanceof String)
72 | {
73 | return _Debug_charColor(ansi, "'" + _Debug_addSlashes(value, true) + "'");
74 | }
75 |
76 | if (typeof value === 'string')
77 | {
78 | return _Debug_stringColor(ansi, '"' + _Debug_addSlashes(value, false) + '"');
79 | }
80 |
81 | if (typeof value === 'object' && '$' in value)
82 | {
83 | var tag = value.$;
84 |
85 | if (typeof tag === 'number')
86 | {
87 | return _Debug_internalColor(ansi, '');
88 | }
89 |
90 | if (tag[0] === '#')
91 | {
92 | var output = [];
93 | for (var k in value)
94 | {
95 | if (k === '$') continue;
96 | output.push(_Debug_toAnsiString(ansi, value[k]));
97 | }
98 | return '(' + output.join(',') + ')';
99 | }
100 |
101 | if (tag === 'Set_elm_builtin')
102 | {
103 | return _Debug_ctorColor(ansi, 'Set')
104 | + _Debug_fadeColor(ansi, '.fromList') + ' '
105 | + _Debug_toAnsiString(ansi, __Set_toList(value));
106 | }
107 |
108 | if (tag === 'RBNode_elm_builtin' || tag === 'RBEmpty_elm_builtin')
109 | {
110 | return _Debug_ctorColor(ansi, 'Dict')
111 | + _Debug_fadeColor(ansi, '.fromList') + ' '
112 | + _Debug_toAnsiString(ansi, __Dict_toList(value));
113 | }
114 |
115 | if (tag === 'Array_elm_builtin')
116 | {
117 | return _Debug_ctorColor(ansi, 'Array')
118 | + _Debug_fadeColor(ansi, '.fromList') + ' '
119 | + _Debug_toAnsiString(ansi, __Array_toList(value));
120 | }
121 |
122 | if (tag === '::' || tag === '[]')
123 | {
124 | var output = '[';
125 |
126 | value.b && (output += _Debug_toAnsiString(ansi, value.a), value = value.b)
127 |
128 | for (; value.b; value = value.b) // WHILE_CONS
129 | {
130 | output += ',' + _Debug_toAnsiString(ansi, value.a);
131 | }
132 | return output + ']';
133 | }
134 |
135 | var output = '';
136 | for (var i in value)
137 | {
138 | if (i === '$') continue;
139 | var str = _Debug_toAnsiString(ansi, value[i]);
140 | var c0 = str[0];
141 | var parenless = c0 === '{' || c0 === '(' || c0 === '[' || c0 === '<' || c0 === '"' || str.indexOf(' ') < 0;
142 | output += ' ' + (parenless ? str : '(' + str + ')');
143 | }
144 | return _Debug_ctorColor(ansi, tag) + output;
145 | }
146 |
147 | if (typeof value === 'object')
148 | {
149 | var output = [];
150 | for (var key in value)
151 | {
152 | var field = key[0] === '_' ? key.slice(1) : key;
153 | output.push(_Debug_fadeColor(ansi, field) + ' = ' + _Debug_toAnsiString(ansi, value[key]));
154 | }
155 | if (output.length === 0)
156 | {
157 | return '{}';
158 | }
159 | return '{ ' + output.join(', ') + ' }';
160 | }
161 |
162 | return _Debug_internalColor(ansi, '');
163 | }
164 |
165 | function _Debug_addSlashes(str, isChar)
166 | {
167 | var s = str
168 | .replace(/\\/g, '\\\\')
169 | .replace(/\n/g, '\\n')
170 | .replace(/\t/g, '\\t')
171 | .replace(/\r/g, '\\r')
172 | .replace(/\v/g, '\\v')
173 | .replace(/\0/g, '\\0');
174 |
175 | if (isChar)
176 | {
177 | return s.replace(/\'/g, '\\\'');
178 | }
179 | else
180 | {
181 | return s.replace(/\"/g, '\\"');
182 | }
183 | }
184 |
185 | function _Debug_ctorColor(ansi, string)
186 | {
187 | return ansi ? '\x1b[96m' + string + '\x1b[0m' : string;
188 | }
189 |
190 | function _Debug_numberColor(ansi, string)
191 | {
192 | return ansi ? '\x1b[95m' + string + '\x1b[0m' : string;
193 | }
194 |
195 | function _Debug_stringColor(ansi, string)
196 | {
197 | return ansi ? '\x1b[93m' + string + '\x1b[0m' : string;
198 | }
199 |
200 | function _Debug_charColor(ansi, string)
201 | {
202 | return ansi ? '\x1b[92m' + string + '\x1b[0m' : string;
203 | }
204 |
205 | function _Debug_fadeColor(ansi, string)
206 | {
207 | return ansi ? '\x1b[37m' + string + '\x1b[0m' : string;
208 | }
209 |
210 | function _Debug_internalColor(ansi, string)
211 | {
212 | return ansi ? '\x1b[94m' + string + '\x1b[0m' : string;
213 | }
214 |
--------------------------------------------------------------------------------
/src/Set.elm:
--------------------------------------------------------------------------------
1 | module Set exposing
2 | ( Set
3 | , empty, singleton, insert, remove
4 | , isEmpty, member, size
5 | , union, intersect, diff
6 | , toList, fromList
7 | , map, foldl, foldr, filter, partition
8 | )
9 |
10 | {-| A set of unique values. The values can be any comparable type. This
11 | includes `Int`, `Float`, `Time`, `Char`, `String`, and tuples or lists
12 | of comparable types.
13 |
14 | Insert, remove, and query operations all take *O(log n)* time.
15 |
16 | # Sets
17 | @docs Set
18 |
19 | # Build
20 | @docs empty, singleton, insert, remove
21 |
22 | # Query
23 | @docs isEmpty, member, size
24 |
25 | # Combine
26 | @docs union, intersect, diff
27 |
28 | # Lists
29 | @docs toList, fromList
30 |
31 | # Transform
32 | @docs map, foldl, foldr, filter, partition
33 |
34 | -}
35 |
36 | import Basics exposing (Bool, Int)
37 | import Dict
38 | import List exposing ((::))
39 | import Maybe exposing (Maybe(..))
40 |
41 |
42 | {-| Represents a set of unique values. So `(Set Int)` is a set of integers and
43 | `(Set String)` is a set of strings.
44 | -}
45 | type Set t =
46 | Set_elm_builtin (Dict.Dict t ())
47 |
48 |
49 | {-| Create an empty set.
50 | -}
51 | empty : Set a
52 | empty =
53 | Set_elm_builtin Dict.empty
54 |
55 |
56 | {-| Create a set with one value.
57 | -}
58 | singleton : comparable -> Set comparable
59 | singleton key =
60 | Set_elm_builtin (Dict.singleton key ())
61 |
62 |
63 | {-| Insert a value into a set.
64 | -}
65 | insert : comparable -> Set comparable -> Set comparable
66 | insert key (Set_elm_builtin dict) =
67 | Set_elm_builtin (Dict.insert key () dict)
68 |
69 |
70 | {-| Remove a value from a set. If the value is not found, no changes are made.
71 | -}
72 | remove : comparable -> Set comparable -> Set comparable
73 | remove key (Set_elm_builtin dict) =
74 | Set_elm_builtin (Dict.remove key dict)
75 |
76 |
77 | {-| Determine if a set is empty.
78 | -}
79 | isEmpty : Set a -> Bool
80 | isEmpty (Set_elm_builtin dict) =
81 | Dict.isEmpty dict
82 |
83 |
84 | {-| Determine if a value is in a set.
85 | -}
86 | member : comparable -> Set comparable -> Bool
87 | member key (Set_elm_builtin dict) =
88 | Dict.member key dict
89 |
90 |
91 | {-| Determine the number of elements in a set.
92 | -}
93 | size : Set a -> Int
94 | size (Set_elm_builtin dict) =
95 | Dict.size dict
96 |
97 |
98 | {-| Get the union of two sets. Keep all values.
99 | -}
100 | union : Set comparable -> Set comparable -> Set comparable
101 | union (Set_elm_builtin dict1) (Set_elm_builtin dict2) =
102 | Set_elm_builtin (Dict.union dict1 dict2)
103 |
104 |
105 | {-| Get the intersection of two sets. Keeps values that appear in both sets.
106 | -}
107 | intersect : Set comparable -> Set comparable -> Set comparable
108 | intersect (Set_elm_builtin dict1) (Set_elm_builtin dict2) =
109 | Set_elm_builtin (Dict.intersect dict1 dict2)
110 |
111 |
112 | {-| Get the difference between the first set and the second. Keeps values
113 | that do not appear in the second set.
114 | -}
115 | diff : Set comparable -> Set comparable -> Set comparable
116 | diff (Set_elm_builtin dict1) (Set_elm_builtin dict2) =
117 | Set_elm_builtin (Dict.diff dict1 dict2)
118 |
119 |
120 | {-| Convert a set into a list, sorted from lowest to highest.
121 | -}
122 | toList : Set a -> List a
123 | toList (Set_elm_builtin dict) =
124 | Dict.keys dict
125 |
126 |
127 | {-| Convert a list into a set, removing any duplicates.
128 | -}
129 | fromList : List comparable -> Set comparable
130 | fromList list =
131 | List.foldl insert empty list
132 |
133 |
134 | {-| Fold over the values in a set, in order from lowest to highest.
135 | -}
136 | foldl : (a -> b -> b) -> b -> Set a -> b
137 | foldl func initialState (Set_elm_builtin dict) =
138 | Dict.foldl (\key _ state -> func key state) initialState dict
139 |
140 |
141 | {-| Fold over the values in a set, in order from highest to lowest.
142 | -}
143 | foldr : (comparable -> b -> b) -> b -> Set comparable -> b
144 | foldr func initialState (Set_elm_builtin dict) =
145 | Dict.foldr (\key _ state -> func key state) initialState dict
146 |
147 |
148 | {-| Map a function onto a set, creating a new set with no duplicates.
149 | -}
150 | map : (comparable -> comparable2) -> Set comparable -> Set comparable2
151 | map func set =
152 | fromList (foldl (\x xs -> func x :: xs) [] set)
153 |
154 |
155 | {-| Only keep elements that pass the given test.
156 |
157 | import Set exposing (Set)
158 |
159 | numbers : Set Int
160 | numbers =
161 | Set.fromList [-2,-1,0,1,2]
162 |
163 | positives : Set Int
164 | positives =
165 | Set.filter (\x -> x > 0) numbers
166 |
167 | -- positives == Set.fromList [1,2]
168 | -}
169 | filter : (comparable -> Bool) -> Set comparable -> Set comparable
170 | filter isGood (Set_elm_builtin dict) =
171 | Set_elm_builtin (Dict.filter (\key _ -> isGood key) dict)
172 |
173 |
174 | {-| Create two new sets. The first contains all the elements that passed the
175 | given test, and the second contains all the elements that did not.
176 | -}
177 | partition : (comparable -> Bool) -> Set comparable -> (Set comparable, Set comparable)
178 | partition isGood (Set_elm_builtin dict) =
179 | let
180 | (dict1, dict2) =
181 | Dict.partition (\key _ -> isGood key) dict
182 | in
183 | (Set_elm_builtin dict1, Set_elm_builtin dict2)
184 |
--------------------------------------------------------------------------------
/src/Elm/JsArray.elm:
--------------------------------------------------------------------------------
1 | module Elm.JsArray
2 | exposing
3 | ( JsArray
4 | , empty
5 | , singleton
6 | , length
7 | , initialize
8 | , initializeFromList
9 | , unsafeGet
10 | , unsafeSet
11 | , push
12 | , foldl
13 | , foldr
14 | , map
15 | , indexedMap
16 | , slice
17 | , appendN
18 | )
19 |
20 | {-| This library provides an immutable version of native javascript arrays.
21 |
22 | NOTE: All manipulations causes a copy of the entire array, this can be slow.
23 | For general purpose use, try the `Array` module instead.
24 |
25 | # Arrays
26 | @docs JsArray
27 |
28 | # Creation
29 | @docs empty, singleton, initialize, listInitialize
30 |
31 | # Basics
32 | @docs length, unsafeGet, unsafeSet, push
33 |
34 | # Transformation
35 | @docs foldl, foldr, map, slice, merge
36 |
37 | -}
38 |
39 |
40 | import Basics exposing (Int)
41 | import Elm.Kernel.JsArray
42 |
43 |
44 |
45 | {-| Representation of a javascript array.
46 | -}
47 | type JsArray a
48 | = JsArray a
49 |
50 |
51 | {-| Return an empty array.
52 | -}
53 | empty : JsArray a
54 | empty =
55 | Elm.Kernel.JsArray.empty
56 |
57 |
58 | {-| Return an array containing a single value.
59 | -}
60 | singleton : a -> JsArray a
61 | singleton =
62 | Elm.Kernel.JsArray.singleton
63 |
64 |
65 | {-| Return the length of the array.
66 | -}
67 | length : JsArray a -> Int
68 | length =
69 | Elm.Kernel.JsArray.length
70 |
71 |
72 | {-| Initialize an array. `initalize n offset fn` creates an array of length `n`
73 | with the element at index `i` initialized to the result of `(f (i + offset))`.
74 |
75 | The offset parameter is there so one can avoid creating a closure for this use
76 | case. This is an optimization that has proved useful in the `Array` module.
77 |
78 | initialize 3 5 identity == [5,6,7]
79 | -}
80 | initialize : Int -> Int -> (Int -> a) -> JsArray a
81 | initialize =
82 | Elm.Kernel.JsArray.initialize
83 |
84 |
85 | {-| Initialize an array from a list. `initializeFromList n ls` creates an array of,
86 | at most, `n` elements from the list. The return value is a tuple containing the
87 | created array as well as a list without the first `n` elements.
88 |
89 | This function was created specifically for the `Array` module, which never wants
90 | to create `JsArray`s above a certain size. That being said, because every
91 | manipulation of `JsArray` results in a copy, users should always try to keep
92 | these as small as possible. The `n` parameter should always be set to a
93 | reasonably small value.
94 | -}
95 | initializeFromList : Int -> List a -> ( JsArray a, List a )
96 | initializeFromList =
97 | Elm.Kernel.JsArray.initializeFromList
98 |
99 |
100 | {-| Returns the element at the given index.
101 |
102 | WARNING: This function does not perform bounds checking.
103 | Make sure you know the index is within bounds when using this function.
104 | -}
105 | unsafeGet : Int -> JsArray a -> a
106 | unsafeGet =
107 | Elm.Kernel.JsArray.unsafeGet
108 |
109 |
110 | {-| Sets the element at the given index.
111 |
112 | WARNING: This function does not perform bounds checking.
113 | Make sure you know the index is within bounds when using this function.
114 | -}
115 | unsafeSet : Int -> a -> JsArray a -> JsArray a
116 | unsafeSet =
117 | Elm.Kernel.JsArray.unsafeSet
118 |
119 |
120 | {-| Push an element onto the array.
121 | -}
122 | push : a -> JsArray a -> JsArray a
123 | push =
124 | Elm.Kernel.JsArray.push
125 |
126 |
127 | {-| Reduce the array from the left.
128 | -}
129 | foldl : (a -> b -> b) -> b -> JsArray a -> b
130 | foldl =
131 | Elm.Kernel.JsArray.foldl
132 |
133 |
134 | {-| Reduce the array from the right.
135 | -}
136 | foldr : (a -> b -> b) -> b -> JsArray a -> b
137 | foldr =
138 | Elm.Kernel.JsArray.foldr
139 |
140 |
141 | {-| Apply a function on every element in an array.
142 | -}
143 | map : (a -> b) -> JsArray a -> JsArray b
144 | map =
145 | Elm.Kernel.JsArray.map
146 |
147 |
148 | {-| Apply a function on every element and its index in an array.
149 | An offset allows to modify the index passed to the function.
150 |
151 | indexedMap (,) 5 (repeat 3 3) == Array [(5,3), (6,3), (7,3)]
152 | -}
153 | indexedMap : (Int -> a -> b) -> Int -> JsArray a -> JsArray b
154 | indexedMap =
155 | Elm.Kernel.JsArray.indexedMap
156 |
157 |
158 | {-| Get a sub section of an array: `(slice start end array)`.
159 | The `start` is a zero-based index where we will start our slice.
160 | The `end` is a zero-based index that indicates the end of the slice.
161 | The slice extracts up to, but no including, the `end`.
162 |
163 | Both `start` and `end` can be negative, indicating an offset from the end
164 | of the array. Popping the last element of the array is therefore:
165 | `slice 0 -1 arr`.
166 |
167 | In the case of an impossible slice, the empty array is returned.
168 | -}
169 | slice : Int -> Int -> JsArray a -> JsArray a
170 | slice =
171 | Elm.Kernel.JsArray.slice
172 |
173 |
174 | {-| Appends `n` elements from array `b` onto array `a`: `(appendN n a b)`.
175 |
176 | The `n` parameter is required by the `Array` module, which never wants to
177 | create `JsArray`s above a certain size, even when appending.
178 | -}
179 | appendN : Int -> JsArray a -> JsArray a -> JsArray a
180 | appendN =
181 | Elm.Kernel.JsArray.appendN
182 |
--------------------------------------------------------------------------------
/tests/Test/Dict.elm:
--------------------------------------------------------------------------------
1 | module Test.Dict exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Dict
5 | import List
6 | import Maybe exposing (..)
7 | import Test exposing (..)
8 | import Expect
9 |
10 |
11 | animals : Dict.Dict String String
12 | animals =
13 | Dict.fromList [ ( "Tom", "cat" ), ( "Jerry", "mouse" ) ]
14 |
15 |
16 | tests : Test
17 | tests =
18 | let
19 | buildTests =
20 | describe "build Tests"
21 | [ test "empty" <| \() -> Expect.equal (Dict.fromList []) (Dict.empty)
22 | , test "singleton" <| \() -> Expect.equal (Dict.fromList [ ( "k", "v" ) ]) (Dict.singleton "k" "v")
23 | , test "insert" <| \() -> Expect.equal (Dict.fromList [ ( "k", "v" ) ]) (Dict.insert "k" "v" Dict.empty)
24 | , test "insert replace" <| \() -> Expect.equal (Dict.fromList [ ( "k", "vv" ) ]) (Dict.insert "k" "vv" (Dict.singleton "k" "v"))
25 | , test "update" <| \() -> Expect.equal (Dict.fromList [ ( "k", "vv" ) ]) (Dict.update "k" (\v -> Just "vv") (Dict.singleton "k" "v"))
26 | , test "update Nothing" <| \() -> Expect.equal Dict.empty (Dict.update "k" (\v -> Nothing) (Dict.singleton "k" "v"))
27 | , test "remove" <| \() -> Expect.equal Dict.empty (Dict.remove "k" (Dict.singleton "k" "v"))
28 | , test "remove not found" <| \() -> Expect.equal (Dict.singleton "k" "v") (Dict.remove "kk" (Dict.singleton "k" "v"))
29 | ]
30 |
31 | queryTests =
32 | describe "query Tests"
33 | [ test "member 1" <| \() -> Expect.equal True (Dict.member "Tom" animals)
34 | , test "member 2" <| \() -> Expect.equal False (Dict.member "Spike" animals)
35 | , test "get 1" <| \() -> Expect.equal (Just "cat") (Dict.get "Tom" animals)
36 | , test "get 2" <| \() -> Expect.equal Nothing (Dict.get "Spike" animals)
37 | , test "size of empty dictionary" <| \() -> Expect.equal 0 (Dict.size Dict.empty)
38 | , test "size of example dictionary" <| \() -> Expect.equal 2 (Dict.size animals)
39 | ]
40 |
41 | combineTests =
42 | describe "combine Tests"
43 | [ test "union" <| \() -> Expect.equal animals (Dict.union (Dict.singleton "Jerry" "mouse") (Dict.singleton "Tom" "cat"))
44 | , test "union collison" <| \() -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.union (Dict.singleton "Tom" "cat") (Dict.singleton "Tom" "mouse"))
45 | , test "intersect" <| \() -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.intersect animals (Dict.singleton "Tom" "cat"))
46 | , test "diff" <| \() -> Expect.equal (Dict.singleton "Jerry" "mouse") (Dict.diff animals (Dict.singleton "Tom" "cat"))
47 | ]
48 |
49 | transformTests =
50 | describe "transform Tests"
51 | [ test "filter" <| \() -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.filter (\k v -> k == "Tom") animals)
52 | , test "partition" <| \() -> Expect.equal ( Dict.singleton "Tom" "cat", Dict.singleton "Jerry" "mouse" ) (Dict.partition (\k v -> k == "Tom") animals)
53 | ]
54 |
55 | mergeTests =
56 | let
57 | insertBoth key leftVal rightVal dict =
58 | Dict.insert key (leftVal ++ rightVal) dict
59 |
60 | s1 =
61 | Dict.empty |> Dict.insert "u1" [ 1 ]
62 |
63 | s2 =
64 | Dict.empty |> Dict.insert "u2" [ 2 ]
65 |
66 | s23 =
67 | Dict.empty |> Dict.insert "u2" [ 3 ]
68 |
69 | b1 =
70 | List.map (\i -> ( i, [ i ] )) (List.range 1 10) |> Dict.fromList
71 |
72 | b2 =
73 | List.map (\i -> ( i, [ i ] )) (List.range 5 15) |> Dict.fromList
74 |
75 | bExpected =
76 | [ ( 1, [ 1 ] ), ( 2, [ 2 ] ), ( 3, [ 3 ] ), ( 4, [ 4 ] ), ( 5, [ 5, 5 ] ), ( 6, [ 6, 6 ] ), ( 7, [ 7, 7 ] ), ( 8, [ 8, 8 ] ), ( 9, [ 9, 9 ] ), ( 10, [ 10, 10 ] ), ( 11, [ 11 ] ), ( 12, [ 12 ] ), ( 13, [ 13 ] ), ( 14, [ 14 ] ), ( 15, [ 15 ] ) ]
77 | in
78 | describe "merge Tests"
79 | [ test "merge empties" <|
80 | \() ->
81 | Expect.equal (Dict.empty)
82 | (Dict.merge Dict.insert insertBoth Dict.insert Dict.empty Dict.empty Dict.empty)
83 | , test "merge singletons in order" <|
84 | \() ->
85 | Expect.equal [ ( "u1", [ 1 ] ), ( "u2", [ 2 ] ) ]
86 | ((Dict.merge Dict.insert insertBoth Dict.insert s1 s2 Dict.empty) |> Dict.toList)
87 | , test "merge singletons out of order" <|
88 | \() ->
89 | Expect.equal [ ( "u1", [ 1 ] ), ( "u2", [ 2 ] ) ]
90 | ((Dict.merge Dict.insert insertBoth Dict.insert s2 s1 Dict.empty) |> Dict.toList)
91 | , test "merge with duplicate key" <|
92 | \() ->
93 | Expect.equal [ ( "u2", [ 2, 3 ] ) ]
94 | ((Dict.merge Dict.insert insertBoth Dict.insert s2 s23 Dict.empty) |> Dict.toList)
95 | , test "partially overlapping" <|
96 | \() ->
97 | Expect.equal bExpected
98 | ((Dict.merge Dict.insert insertBoth Dict.insert b1 b2 Dict.empty) |> Dict.toList)
99 | ]
100 | in
101 | describe "Dict Tests"
102 | [ buildTests
103 | , queryTests
104 | , combineTests
105 | , transformTests
106 | , mergeTests
107 | ]
108 |
--------------------------------------------------------------------------------
/tests/Test/String.elm:
--------------------------------------------------------------------------------
1 | module Test.String exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import List
5 | import Maybe exposing (..)
6 | import Result exposing (Result(..))
7 | import String
8 | import Test exposing (..)
9 | import Expect
10 |
11 |
12 | tests : Test
13 | tests =
14 | let
15 | simpleTests =
16 | describe "Simple Stuff"
17 | [ test "is empty" <| \() -> Expect.equal True (String.isEmpty "")
18 | , test "is not empty" <| \() -> Expect.equal True (not (String.isEmpty ("the world")))
19 | , test "length" <| \() -> Expect.equal 11 (String.length "innumerable")
20 | , test "endsWith" <| \() -> Expect.equal True <| String.endsWith "ship" "spaceship"
21 | , test "reverse" <| \() -> Expect.equal "desserts" (String.reverse "stressed")
22 | , test "repeat" <| \() -> Expect.equal "hahaha" (String.repeat 3 "ha")
23 | , test "indexes" <| \() -> Expect.equal [ 0, 2 ] (String.indexes "a" "aha")
24 | , test "empty indexes" <| \() -> Expect.equal [] (String.indexes "" "aha")
25 | ]
26 |
27 | combiningTests =
28 | describe "Combining Strings"
29 | [ test "uncons non-empty" <| \() -> Expect.equal (Just ( 'a', "bc" )) (String.uncons "abc")
30 | , test "uncons empty" <| \() -> Expect.equal Nothing (String.uncons "")
31 | , test "append 1" <| \() -> Expect.equal "butterfly" (String.append "butter" "fly")
32 | , test "append 2" <| \() -> Expect.equal "butter" (String.append "butter" "")
33 | , test "append 3" <| \() -> Expect.equal "butter" (String.append "" "butter")
34 | , test "concat" <| \() -> Expect.equal "nevertheless" (String.concat [ "never", "the", "less" ])
35 | , test "split commas" <| \() -> Expect.equal [ "cat", "dog", "cow" ] (String.split "," "cat,dog,cow")
36 | , test "split slashes" <| \() -> Expect.equal [ "home", "steve", "Desktop", "" ] (String.split "/" "home/steve/Desktop/")
37 | , test "join spaces" <| \() -> Expect.equal "cat dog cow" (String.join " " [ "cat", "dog", "cow" ])
38 | , test "join slashes" <| \() -> Expect.equal "home/steve/Desktop" (String.join "/" [ "home", "steve", "Desktop" ])
39 | , test "slice 1" <| \() -> Expect.equal "c" (String.slice 2 3 "abcd")
40 | , test "slice 2" <| \() -> Expect.equal "abc" (String.slice 0 3 "abcd")
41 | , test "slice 3" <| \() -> Expect.equal "abc" (String.slice 0 -1 "abcd")
42 | , test "slice 4" <| \() -> Expect.equal "cd" (String.slice -2 4 "abcd")
43 | ]
44 |
45 | intTests =
46 | describe "String.toInt"
47 | [ goodInt "1234" 1234
48 | , goodInt "+1234" 1234
49 | , goodInt "-1234" -1234
50 | , badInt "1.34"
51 | , badInt "1e31"
52 | , badInt "123a"
53 | , goodInt "0123" 123
54 | , goodInt "0x001A" 26
55 | , goodInt "0x001a" 26
56 | , goodInt "0xBEEF" 48879
57 | , badInt "0x12.0"
58 | , badInt "0x12an"
59 | ]
60 |
61 | floatTests =
62 | describe "String.toFloat"
63 | [ goodFloat "123" 123
64 | , goodFloat "3.14" 3.14
65 | , goodFloat "+3.14" 3.14
66 | , goodFloat "-3.14" -3.14
67 | , goodFloat "0.12" 0.12
68 | , goodFloat ".12" 0.12
69 | , goodFloat "1e-42" 1e-42
70 | , goodFloat "6.022e23" 6.022e23
71 | , goodFloat "6.022E23" 6.022e23
72 | , goodFloat "6.022e+23" 6.022e23
73 | , badFloat "6.022e"
74 | , badFloat "6.022n"
75 | , badFloat "6.022.31"
76 | ]
77 |
78 | encodingTests =
79 | describe "UTF-16 Encoding"
80 | [ test "reverse 1" <| \() -> Expect.equal "𝌆c𝌆b𝌆a𝌆" (String.reverse "𝌆a𝌆b𝌆c𝌆")
81 | , test "reverse 2" <| \() -> Expect.equal "nàm" (String.reverse "màn")
82 | , test "reverse 3" <| \() -> Expect.equal "😣ba" (String.reverse "ab😣")
83 | , test "filter" <| \() -> Expect.equal "mànabc" (String.filter (\c -> c /= '😣') "màn😣abc")
84 | , test "toList" <| \() -> Expect.equal ['𝌆', 'a', '𝌆', 'b', '𝌆'] (String.toList "𝌆a𝌆b𝌆")
85 | , test "uncons" <| \() -> Expect.equal (Just ( '😃', "bc" )) (String.uncons "😃bc")
86 | , test "map 1" <| \() -> Expect.equal "aaa" (String.map (\_ -> 'a') "😃😃😃")
87 | , test "map 2" <| \() -> Expect.equal "😃😃😃" (String.map (\_ -> '😃') "aaa")
88 | , test "foldl" <| \() -> Expect.equal 3 (String.foldl (\_ c -> c + 1) 0 "😃😃😃")
89 | , test "foldr" <| \() -> Expect.equal 3 (String.foldr (\_ c -> c + 1) 0 "😃😃😃")
90 | , test "all" <| \() -> Expect.equal True (String.all ((==) '😃') "😃😃😃")
91 | , test "any" <| \() -> Expect.equal True (String.any ((==) '😃') "abc😃123")
92 | ]
93 | in
94 | describe "String" [ simpleTests, combiningTests, intTests, floatTests, encodingTests ]
95 |
96 |
97 |
98 | -- NUMBER HELPERS
99 |
100 |
101 | goodInt : String -> Int -> Test
102 | goodInt str int =
103 | test str <| \_ ->
104 | Expect.equal (Ok int) (String.toInt str)
105 |
106 |
107 | badInt : String -> Test
108 | badInt str =
109 | test str <| \_ ->
110 | Expect.equal
111 | (Err ("could not convert string '" ++ str ++ "' to an Int"))
112 | (String.toInt str)
113 |
114 |
115 | goodFloat : String -> Float -> Test
116 | goodFloat str float =
117 | test str <| \_ ->
118 | Expect.equal (Ok float) (String.toFloat str)
119 |
120 |
121 | badFloat : String -> Test
122 | badFloat str =
123 | test str <| \_ ->
124 | Expect.equal
125 | (Err ("could not convert string '" ++ str ++ "' to a Float"))
126 | (String.toFloat str)
127 |
--------------------------------------------------------------------------------
/src/Char.elm:
--------------------------------------------------------------------------------
1 | module Char exposing
2 | ( Char
3 | , isUpper, isLower, isAlpha, isAlphaNum
4 | , isDigit, isOctDigit, isHexDigit
5 | , toUpper, toLower, toLocaleUpper, toLocaleLower
6 | , toCode, fromCode
7 | )
8 |
9 | {-| Functions for working with characters. Character literals are enclosed in
10 | `'a'` pair of single quotes.
11 |
12 | # Characters
13 | @docs Char
14 |
15 | # ASCII Letters
16 | @docs isUpper, isLower, isAlpha, isAlphaNum
17 |
18 | # Digits
19 | @docs isDigit, isOctDigit, isHexDigit
20 |
21 | # Conversion
22 | @docs toUpper, toLower, toLocaleUpper, toLocaleLower
23 |
24 | # Unicode Code Points
25 | @docs toCode, fromCode
26 | -}
27 |
28 | import Basics exposing (Bool, Int, (&&), (||), (>=), (<=))
29 | import Elm.Kernel.Char
30 |
31 |
32 |
33 | -- CHAR
34 |
35 |
36 | {-| A `Char` is a single [unicode][u] character:
37 |
38 | 'a'
39 | '0'
40 | 'Z'
41 | '?'
42 | '"'
43 | 'Σ'
44 | '🙈'
45 |
46 | '\t'
47 | '\"'
48 | '\''
49 | '\u{1F648}' -- '🙈'
50 |
51 | **Note 1:** You _cannot_ use single quotes around multiple characters like in
52 | JavaScript. This is how we distinguish [`String`](String#String) and `Char`
53 | values in syntax.
54 |
55 | **Note 2:** You can use the unicode escapes from `\u{0000}` to `\u{10FFFF}` to
56 | represent characters by their code point. You can also include the unicode
57 | characters directly. Using the escapes can be better if you need one of the
58 | many whitespace characters with different widths.
59 |
60 | [u]: https://en.wikipedia.org/wiki/Unicode
61 | -}
62 | type Char = Char -- NOTE: The compiler provides the real implementation.
63 |
64 |
65 |
66 | -- CLASSIFICATION
67 |
68 |
69 | {-| Detect upper case ASCII characters.
70 |
71 | isUpper 'A' == True
72 | isUpper 'B' == True
73 | ...
74 | isUpper 'Z' == True
75 |
76 | isUpper '0' == False
77 | isUpper 'a' == False
78 | isUpper '-' == False
79 | isUpper 'Σ' == False
80 | -}
81 | isUpper : Char -> Bool
82 | isUpper char =
83 | let
84 | code =
85 | toCode char
86 | in
87 | code <= 0x5A && 0x41 <= code
88 |
89 |
90 | {-| Detect lower case ASCII characters.
91 |
92 | isLower 'a' == True
93 | isLower 'b' == True
94 | ...
95 | isLower 'z' == True
96 |
97 | isLower '0' == False
98 | isLower 'A' == False
99 | isLower '-' == False
100 | isLower 'π' == False
101 | -}
102 | isLower : Char -> Bool
103 | isLower char =
104 | let
105 | code =
106 | toCode char
107 | in
108 | 0x61 <= code && code <= 0x7A
109 |
110 |
111 | {-| Detect upper case and lower case ASCII characters.
112 |
113 | isAlpha 'a' == True
114 | isAlpha 'b' == True
115 | isAlpha 'E' == True
116 | isAlpha 'Y' == True
117 |
118 | isAlpha '0' == False
119 | isAlpha '-' == False
120 | isAlpha 'π' == False
121 | -}
122 | isAlpha : Char -> Bool
123 | isAlpha char =
124 | isLower char || isUpper char
125 |
126 |
127 | {-| Detect upper case and lower case ASCII characters.
128 |
129 | isAlphaNum 'a' == True
130 | isAlphaNum 'b' == True
131 | isAlphaNum 'E' == True
132 | isAlphaNum 'Y' == True
133 | isAlphaNum '0' == True
134 | isAlphaNum '7' == True
135 |
136 | isAlphaNum '-' == False
137 | isAlphaNum 'π' == False
138 | -}
139 | isAlphaNum : Char -> Bool
140 | isAlphaNum char =
141 | isLower char || isUpper char || isDigit char
142 |
143 |
144 | {-| Detect digits `0123456789`
145 |
146 | isDigit '0' == True
147 | isDigit '1' == True
148 | ...
149 | isDigit '9' == True
150 |
151 | isDigit 'a' == False
152 | isDigit 'b' == False
153 | isDigit 'A' == False
154 | -}
155 | isDigit : Char -> Bool
156 | isDigit char =
157 | let
158 | code =
159 | toCode char
160 | in
161 | code <= 0x39 && 0x30 <= code
162 |
163 |
164 | {-| Detect octal digits `01234567`
165 |
166 | isOctDigit '0' == True
167 | isOctDigit '1' == True
168 | ...
169 | isOctDigit '7' == True
170 |
171 | isOctDigit '8' == False
172 | isOctDigit 'a' == False
173 | isOctDigit 'A' == False
174 | -}
175 | isOctDigit : Char -> Bool
176 | isOctDigit char =
177 | let
178 | code =
179 | toCode char
180 | in
181 | code <= 0x37 && 0x30 <= code
182 |
183 |
184 | {-| Detect hexidecimal digits `0123456789abcdefABCDEF`
185 | -}
186 | isHexDigit : Char -> Bool
187 | isHexDigit char =
188 | let
189 | code =
190 | toCode char
191 | in
192 | (0x30 <= code && code <= 0x39)
193 | || (0x41 <= code && code <= 0x46)
194 | || (0x61 <= code && code <= 0x66)
195 |
196 |
197 |
198 | -- CONVERSIONS
199 |
200 |
201 | {-| Convert to upper case. -}
202 | toUpper : Char -> Char
203 | toUpper =
204 | Elm.Kernel.Char.toUpper
205 |
206 |
207 | {-| Convert to lower case. -}
208 | toLower : Char -> Char
209 | toLower =
210 | Elm.Kernel.Char.toLower
211 |
212 |
213 | {-| Convert to upper case, according to any locale-specific case mappings. -}
214 | toLocaleUpper : Char -> Char
215 | toLocaleUpper =
216 | Elm.Kernel.Char.toLocaleUpper
217 |
218 |
219 | {-| Convert to lower case, according to any locale-specific case mappings. -}
220 | toLocaleLower : Char -> Char
221 | toLocaleLower =
222 | Elm.Kernel.Char.toLocaleLower
223 |
224 |
225 | {-| Convert to the corresponding Unicode [code point][cp].
226 |
227 | [cp]: https://en.wikipedia.org/wiki/Code_point
228 |
229 | toCode 'A' == 65
230 | toCode 'B' == 66
231 | toCode '木' == 0x6728
232 | toCode '𝌆' == 0x1D306
233 | toCode '😃' == 0x1F603
234 | -}
235 | toCode : Char -> Int
236 | toCode =
237 | Elm.Kernel.Char.toCode
238 |
239 |
240 | {-| Convert a Unicode [code point][cp] to a character.
241 |
242 | fromCode 65 == 'A'
243 | fromCode 66 == 'B'
244 | fromCode 0x6728 == '木'
245 | fromCode 0x1D306 == '𝌆'
246 | fromCode 0x1F603 == '😃'
247 | fromCode -1 == '�'
248 |
249 | The full range of unicode is from `0` to `0x10FFFF`. With numbers outside that
250 | range, you get [the replacement character][fffd].
251 |
252 | [cp]: https://en.wikipedia.org/wiki/Code_point
253 | [fffd]: https://en.wikipedia.org/wiki/Specials_(Unicode_block)#Replacement_character
254 | -}
255 | fromCode : Int -> Char
256 | fromCode =
257 | Elm.Kernel.Char.fromCode
258 |
--------------------------------------------------------------------------------
/tests/Test/Maybe.elm:
--------------------------------------------------------------------------------
1 | module Test.Maybe exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Maybe exposing (..)
5 | import Test exposing (..)
6 | import Expect
7 |
8 | tests : Test
9 | tests =
10 | describe "Maybe Tests"
11 |
12 | [ describe "Common Helpers Tests"
13 |
14 | [ describe "withDefault Tests"
15 | [ test "no default used" <|
16 | \() -> Expect.equal 0 (Maybe.withDefault 5 (Just 0))
17 | , test "default used" <|
18 | \() -> Expect.equal 5 (Maybe.withDefault 5 (Nothing))
19 | ]
20 |
21 | , describe "map Tests"
22 | ( let f = (\n -> n + 1) in
23 | [ test "on Just" <|
24 | \() ->
25 | Expect.equal
26 | (Just 1)
27 | (Maybe.map f (Just 0))
28 | , test "on Nothing" <|
29 | \() ->
30 | Expect.equal
31 | Nothing
32 | (Maybe.map f Nothing)
33 | ]
34 | )
35 |
36 | , describe "map2 Tests"
37 | ( let f = (+) in
38 | [ test "on (Just, Just)" <|
39 | \() ->
40 | Expect.equal
41 | (Just 1)
42 | (Maybe.map2 f (Just 0) (Just 1))
43 | , test "on (Just, Nothing)" <|
44 | \() ->
45 | Expect.equal
46 | Nothing
47 | (Maybe.map2 f (Just 0) Nothing)
48 | , test "on (Nothing, Just)" <|
49 | \() ->
50 | Expect.equal
51 | Nothing
52 | (Maybe.map2 f Nothing (Just 0))
53 | ]
54 | )
55 |
56 | , describe "map3 Tests"
57 | ( let f = (\a b c -> a + b + c) in
58 | [ test "on (Just, Just, Just)" <|
59 | \() ->
60 | Expect.equal
61 | (Just 3)
62 | (Maybe.map3 f (Just 1) (Just 1) (Just 1))
63 | , test "on (Just, Just, Nothing)" <|
64 | \() ->
65 | Expect.equal
66 | Nothing
67 | (Maybe.map3 f (Just 1) (Just 1) Nothing)
68 | , test "on (Just, Nothing, Just)" <|
69 | \() ->
70 | Expect.equal
71 | Nothing
72 | (Maybe.map3 f (Just 1) Nothing (Just 1))
73 | , test "on (Nothing, Just, Just)" <|
74 | \() ->
75 | Expect.equal
76 | Nothing
77 | (Maybe.map3 f Nothing (Just 1) (Just 1))
78 | ]
79 | )
80 |
81 | , describe "map4 Tests"
82 | ( let f = (\a b c d -> a + b + c + d) in
83 | [ test "on (Just, Just, Just, Just)" <|
84 | \() ->
85 | Expect.equal
86 | (Just 4)
87 | (Maybe.map4 f (Just 1) (Just 1) (Just 1) (Just 1))
88 | , test "on (Just, Just, Just, Nothing)" <|
89 | \() ->
90 | Expect.equal
91 | Nothing
92 | (Maybe.map4 f (Just 1) (Just 1) (Just 1) Nothing)
93 | , test "on (Just, Just, Nothing, Just)" <|
94 | \() ->
95 | Expect.equal
96 | Nothing
97 | (Maybe.map4 f (Just 1) (Just 1) Nothing (Just 1))
98 | , test "on (Just, Nothing, Just, Just)" <|
99 | \() ->
100 | Expect.equal
101 | Nothing
102 | (Maybe.map4 f (Just 1) Nothing (Just 1) (Just 1))
103 | , test "on (Nothing, Just, Just, Just)" <|
104 | \() ->
105 | Expect.equal
106 | Nothing
107 | (Maybe.map4 f Nothing (Just 1) (Just 1) (Just 1))
108 | ]
109 | )
110 |
111 | , describe "map5 Tests"
112 | ( let f = (\a b c d e -> a + b + c + d + e) in
113 | [ test "on (Just, Just, Just, Just, Just)" <|
114 | \() ->
115 | Expect.equal
116 | (Just 5)
117 | (Maybe.map5 f (Just 1) (Just 1) (Just 1) (Just 1) (Just 1))
118 | , test "on (Just, Just, Just, Just, Nothing)" <|
119 | \() ->
120 | Expect.equal
121 | Nothing
122 | (Maybe.map5 f (Just 1) (Just 1) (Just 1) (Just 1) Nothing)
123 | , test "on (Just, Just, Just, Nothing, Just)" <|
124 | \() ->
125 | Expect.equal
126 | Nothing
127 | (Maybe.map5 f (Just 1) (Just 1) (Just 1) Nothing (Just 1))
128 | , test "on (Just, Just, Nothing, Just, Just)" <|
129 | \() ->
130 | Expect.equal
131 | Nothing
132 | (Maybe.map5 f (Just 1) (Just 1) Nothing (Just 1) (Just 1))
133 | , test "on (Just, Nothing, Just, Just, Just)" <|
134 | \() ->
135 | Expect.equal
136 | Nothing
137 | (Maybe.map5 f (Just 1) Nothing (Just 1) (Just 1) (Just 1))
138 | , test "on (Nothing, Just, Just, Just, Just)" <|
139 | \() ->
140 | Expect.equal
141 | Nothing
142 | (Maybe.map5 f Nothing (Just 1) (Just 1) (Just 1) (Just 1))
143 | ]
144 | )
145 |
146 | ]
147 |
148 | , describe "Chaining Maybes Tests"
149 |
150 | [ describe "andThen Tests"
151 | [ test "succeeding chain" <|
152 | \() ->
153 | Expect.equal
154 | (Just 1)
155 | (Maybe.andThen (\a -> Just a) (Just 1))
156 | , test "failing chain (original Maybe failed)" <|
157 | \() ->
158 | Expect.equal
159 | Nothing
160 | (Maybe.andThen (\a -> Just a) Nothing)
161 | , test "failing chain (chained function failed)" <|
162 | \() ->
163 | Expect.equal
164 | Nothing
165 | (Maybe.andThen (\a -> Nothing) (Just 1))
166 | ]
167 | ]
168 |
169 | ]
170 |
--------------------------------------------------------------------------------
/src/Maybe.elm:
--------------------------------------------------------------------------------
1 | module Maybe exposing
2 | ( Maybe(..)
3 | , andThen
4 | , map, map2, map3, map4, map5
5 | , withDefault
6 | )
7 |
8 | {-| This library fills a bunch of important niches in Elm. A `Maybe` can help
9 | you with optional arguments, error handling, and records with optional fields.
10 |
11 | # Definition
12 | @docs Maybe
13 |
14 | # Common Helpers
15 | @docs withDefault, map, map2, map3, map4, map5
16 |
17 | # Chaining Maybes
18 | @docs andThen
19 | -}
20 |
21 |
22 | import Basics exposing (Bool(..))
23 |
24 |
25 |
26 | {-| Represent values that may or may not exist. It can be useful if you have a
27 | record field that is only filled in sometimes. Or if a function takes a value
28 | sometimes, but does not absolutely need it.
29 |
30 | -- A person, but maybe we do not know their age.
31 | type alias Person =
32 | { name : String
33 | , age : Maybe Int
34 | }
35 |
36 | tom = { name = "Tom", age = Just 42 }
37 | sue = { name = "Sue", age = Nothing }
38 | -}
39 | type Maybe a
40 | = Just a
41 | | Nothing
42 |
43 |
44 | {-| Provide a default value, turning an optional value into a normal
45 | value. This comes in handy when paired with functions like
46 | [`Dict.get`](Dict#get) which gives back a `Maybe`.
47 |
48 | withDefault 100 (Just 42) -- 42
49 | withDefault 100 Nothing -- 100
50 |
51 | withDefault "unknown" (Dict.get "Tom" Dict.empty) -- "unknown"
52 |
53 | -}
54 | withDefault : a -> Maybe a -> a
55 | withDefault default maybe =
56 | case maybe of
57 | Just value -> value
58 | Nothing -> default
59 |
60 |
61 | {-| Transform a `Maybe` value with a given function:
62 |
63 | map sqrt (Just 9) == Just 3
64 | map sqrt Nothing == Nothing
65 |
66 | map sqrt (String.toFloat "9") == Just 3
67 | map sqrt (String.toFloat "x") == Nothing
68 |
69 | -}
70 | map : (a -> b) -> Maybe a -> Maybe b
71 | map f maybe =
72 | case maybe of
73 | Just value ->
74 | Just (f value)
75 |
76 | Nothing ->
77 | Nothing
78 |
79 |
80 | {-| Apply a function if all the arguments are `Just` a value.
81 |
82 | map2 (+) (Just 3) (Just 4) == Just 7
83 | map2 (+) (Just 3) Nothing == Nothing
84 | map2 (+) Nothing (Just 4) == Nothing
85 |
86 | map2 (+) (String.toInt "1") (String.toInt "123") == Just 124
87 | map2 (+) (String.toInt "x") (String.toInt "123") == Nothing
88 | map2 (+) (String.toInt "1") (String.toInt "1.3") == Nothing
89 | -}
90 | map2 : (a -> b -> value) -> Maybe a -> Maybe b -> Maybe value
91 | map2 func ma mb =
92 | case ma of
93 | Nothing ->
94 | Nothing
95 |
96 | Just a ->
97 | case mb of
98 | Nothing ->
99 | Nothing
100 |
101 | Just b ->
102 | Just (func a b)
103 |
104 |
105 | {-|-}
106 | map3 : (a -> b -> c -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe value
107 | map3 func ma mb mc =
108 | case ma of
109 | Nothing ->
110 | Nothing
111 |
112 | Just a ->
113 | case mb of
114 | Nothing ->
115 | Nothing
116 |
117 | Just b ->
118 | case mc of
119 | Nothing ->
120 | Nothing
121 |
122 | Just c ->
123 | Just (func a b c)
124 |
125 |
126 | {-|-}
127 | map4 : (a -> b -> c -> d -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe value
128 | map4 func ma mb mc md =
129 | case ma of
130 | Nothing ->
131 | Nothing
132 |
133 | Just a ->
134 | case mb of
135 | Nothing ->
136 | Nothing
137 |
138 | Just b ->
139 | case mc of
140 | Nothing ->
141 | Nothing
142 |
143 | Just c ->
144 | case md of
145 | Nothing ->
146 | Nothing
147 |
148 | Just d ->
149 | Just (func a b c d)
150 |
151 |
152 | {-|-}
153 | map5 : (a -> b -> c -> d -> e -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe e -> Maybe value
154 | map5 func ma mb mc md me =
155 | case ma of
156 | Nothing ->
157 | Nothing
158 |
159 | Just a ->
160 | case mb of
161 | Nothing ->
162 | Nothing
163 |
164 | Just b ->
165 | case mc of
166 | Nothing ->
167 | Nothing
168 |
169 | Just c ->
170 | case md of
171 | Nothing ->
172 | Nothing
173 |
174 | Just d ->
175 | case me of
176 | Nothing ->
177 | Nothing
178 |
179 | Just e ->
180 | Just (func a b c d e)
181 |
182 |
183 | {-| Chain together many computations that may fail. It is helpful to see its
184 | definition:
185 |
186 | andThen : (a -> Maybe b) -> Maybe a -> Maybe b
187 | andThen callback maybe =
188 | case maybe of
189 | Just value ->
190 | callback value
191 |
192 | Nothing ->
193 | Nothing
194 |
195 | This means we only continue with the callback if things are going well. For
196 | example, say you need to parse some user input as a month:
197 |
198 | parseMonth : String -> Maybe Int
199 | parseMonth userInput =
200 | String.toInt userInput
201 | |> andThen toValidMonth
202 |
203 | toValidMonth : Int -> Maybe Int
204 | toValidMonth month =
205 | if 1 <= month && month <= 12 then
206 | Just month
207 | else
208 | Nothing
209 |
210 | In the `parseMonth` function, if `String.toInt` produces `Nothing` (because
211 | the `userInput` was not an integer) this entire chain of operations will
212 | short-circuit and result in `Nothing`. If `toValidMonth` results in `Nothing`,
213 | again the chain of computations will result in `Nothing`.
214 | -}
215 | andThen : (a -> Maybe b) -> Maybe a -> Maybe b
216 | andThen callback maybeValue =
217 | case maybeValue of
218 | Just value ->
219 | callback value
220 |
221 | Nothing ->
222 | Nothing
223 |
224 |
225 |
226 | -- FOR INTERNAL USE ONLY
227 | --
228 | -- Use `case` expressions for this in Elm code!
229 |
230 |
231 | isJust : Maybe a -> Bool
232 | isJust maybe =
233 | case maybe of
234 | Just _ ->
235 | True
236 |
237 | Nothing ->
238 | False
239 |
240 |
241 | destruct : b -> (a -> b) -> Maybe a -> b
242 | destruct default func maybe =
243 | case maybe of
244 | Just a ->
245 | func a
246 |
247 | Nothing ->
248 | default
249 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/String.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.List exposing (fromArray, toArray, Nil)
4 | import Elm.Kernel.Utils exposing (chr, Tuple2)
5 | import Maybe exposing (Just, Nothing)
6 |
7 | */
8 |
9 |
10 | var _String_cons = F2(function(chr, str)
11 | {
12 | return chr + str;
13 | });
14 |
15 | function _String_uncons(string)
16 | {
17 | var word = string.charCodeAt(0);
18 | return word
19 | ? __Maybe_Just(
20 | 0xD800 <= word && word <= 0xDBFF
21 | ? __Utils_Tuple2(__Utils_chr(string[0] + string[1]), string.slice(2))
22 | : __Utils_Tuple2(__Utils_chr(string[0]), string.slice(1))
23 | )
24 | : __Maybe_Nothing;
25 | }
26 |
27 | var _String_append = F2(function(a, b)
28 | {
29 | return a + b;
30 | });
31 |
32 | function _String_length(str)
33 | {
34 | return str.length;
35 | }
36 |
37 | var _String_map = F2(function(func, string)
38 | {
39 | var len = string.length;
40 | var array = new Array(len);
41 | var i = 0;
42 | while (i < len)
43 | {
44 | var word = string.charCodeAt(i);
45 | if (0xD800 <= word && word <= 0xDBFF)
46 | {
47 | array[i] = func(__Utils_chr(string[i] + string[i+1]));
48 | i += 2;
49 | continue;
50 | }
51 | array[i] = func(__Utils_chr(string[i]));
52 | i++;
53 | }
54 | return array.join('');
55 | });
56 |
57 | var _String_filter = F2(function(isGood, str)
58 | {
59 | var arr = [];
60 | var len = str.length;
61 | var i = 0;
62 | while (i < len)
63 | {
64 | var char = str[i];
65 | var word = str.charCodeAt(i);
66 | i++;
67 | if (0xD800 <= word && word <= 0xDBFF)
68 | {
69 | char += str[i];
70 | i++;
71 | }
72 |
73 | if (isGood(__Utils_chr(char)))
74 | {
75 | arr.push(char);
76 | }
77 | }
78 | return arr.join('');
79 | });
80 |
81 | function _String_reverse(str)
82 | {
83 | var len = str.length;
84 | var arr = new Array(len);
85 | var i = 0;
86 | while (i < len)
87 | {
88 | var word = str.charCodeAt(i);
89 | if (0xD800 <= word && word <= 0xDBFF)
90 | {
91 | arr[len - i] = str[i + 1];
92 | i++;
93 | arr[len - i] = str[i - 1];
94 | i++;
95 | }
96 | else
97 | {
98 | arr[len - i] = str[i];
99 | i++;
100 | }
101 | }
102 | return arr.join('');
103 | }
104 |
105 | var _String_foldl = F3(function(func, state, string)
106 | {
107 | var len = string.length;
108 | var i = 0;
109 | while (i < len)
110 | {
111 | var char = string[i];
112 | var word = string.charCodeAt(i);
113 | i++;
114 | if (0xD800 <= word && word <= 0xDBFF)
115 | {
116 | char += string[i];
117 | i++;
118 | }
119 | state = A2(func, __Utils_chr(char), state);
120 | }
121 | return state;
122 | });
123 |
124 | var _String_foldr = F3(function(func, state, string)
125 | {
126 | var i = string.length;
127 | while (i--)
128 | {
129 | var char = string[i];
130 | var word = string.charCodeAt(i);
131 | if (0xDC00 <= word && word <= 0xDFFF)
132 | {
133 | i--;
134 | char = string[i] + char;
135 | }
136 | state = A2(func, __Utils_chr(char), state);
137 | }
138 | return state;
139 | });
140 |
141 | var _String_split = F2(function(sep, str)
142 | {
143 | return str.split(sep);
144 | });
145 |
146 | var _String_join = F2(function(sep, strs)
147 | {
148 | return strs.join(sep);
149 | });
150 |
151 | var _String_slice = F3(function(start, end, str) {
152 | return str.slice(start, end);
153 | });
154 |
155 | function _String_trim(str)
156 | {
157 | return str.trim();
158 | }
159 |
160 | function _String_trimLeft(str)
161 | {
162 | return str.replace(/^\s+/, '');
163 | }
164 |
165 | function _String_trimRight(str)
166 | {
167 | return str.replace(/\s+$/, '');
168 | }
169 |
170 | function _String_words(str)
171 | {
172 | return __List_fromArray(str.trim().split(/\s+/g));
173 | }
174 |
175 | function _String_lines(str)
176 | {
177 | return __List_fromArray(str.split(/\r\n|\r|\n/g));
178 | }
179 |
180 | function _String_toUpper(str)
181 | {
182 | return str.toUpperCase();
183 | }
184 |
185 | function _String_toLower(str)
186 | {
187 | return str.toLowerCase();
188 | }
189 |
190 | var _String_any = F2(function(isGood, string)
191 | {
192 | var i = string.length;
193 | while (i--)
194 | {
195 | var char = string[i];
196 | var word = string.charCodeAt(i);
197 | if (0xDC00 <= word && word <= 0xDFFF)
198 | {
199 | i--;
200 | char = string[i] + char;
201 | }
202 | if (isGood(__Utils_chr(char)))
203 | {
204 | return true;
205 | }
206 | }
207 | return false;
208 | });
209 |
210 | var _String_all = F2(function(isGood, string)
211 | {
212 | var i = string.length;
213 | while (i--)
214 | {
215 | var char = string[i];
216 | var word = string.charCodeAt(i);
217 | if (0xDC00 <= word && word <= 0xDFFF)
218 | {
219 | i--;
220 | char = string[i] + char;
221 | }
222 | if (!isGood(__Utils_chr(char)))
223 | {
224 | return false;
225 | }
226 | }
227 | return true;
228 | });
229 |
230 | var _String_contains = F2(function(sub, str)
231 | {
232 | return str.indexOf(sub) > -1;
233 | });
234 |
235 | var _String_startsWith = F2(function(sub, str)
236 | {
237 | return str.indexOf(sub) === 0;
238 | });
239 |
240 | var _String_endsWith = F2(function(sub, str)
241 | {
242 | return str.length >= sub.length &&
243 | str.lastIndexOf(sub) === str.length - sub.length;
244 | });
245 |
246 | var _String_indexes = F2(function(sub, str)
247 | {
248 | var subLen = sub.length;
249 |
250 | if (subLen < 1)
251 | {
252 | return __List_Nil;
253 | }
254 |
255 | var i = 0;
256 | var is = [];
257 |
258 | while ((i = str.indexOf(sub, i)) > -1)
259 | {
260 | is.push(i);
261 | i = i + subLen;
262 | }
263 |
264 | return __List_fromArray(is);
265 | });
266 |
267 |
268 | // TO STRING
269 |
270 | function _String_fromNumber(number)
271 | {
272 | return number + '';
273 | }
274 |
275 |
276 | // INT CONVERSIONS
277 |
278 | function _String_toInt(s)
279 | {
280 | var len = s.length;
281 |
282 | // if empty
283 | if (len === 0)
284 | {
285 | return __Maybe_Nothing;
286 | }
287 |
288 | // if hex
289 | var c = s[0];
290 | if (c === '0' && s[1] === 'x')
291 | {
292 | for (var i = 2; i < len; ++i)
293 | {
294 | var c = s[i];
295 | if (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'))
296 | {
297 | continue;
298 | }
299 | return __Maybe_Nothing;
300 | }
301 | return __Maybe_Just(parseInt(s, 16));
302 | }
303 |
304 | // is decimal
305 | if (c > '9' || (c < '0' && ((c !== '-' && c !== '+') || len === 1)))
306 | {
307 | return __Maybe_Nothing;
308 | }
309 | for (var i = 1; i < len; ++i)
310 | {
311 | var c = s[i];
312 | if (c < '0' || '9' < c)
313 | {
314 | return __Maybe_Nothing;
315 | }
316 | }
317 |
318 | return __Maybe_Just(parseInt(s, 10));
319 | }
320 |
321 |
322 | // FLOAT CONVERSIONS
323 |
324 | function _String_toFloat(s)
325 | {
326 | // check if it is a hex, octal, or binary number
327 | if (s.length === 0 || /[\sxbo]/.test(s))
328 | {
329 | return __Maybe_Nothing;
330 | }
331 | var n = +s;
332 | // faster isNaN check
333 | return n === n ? __Maybe_Just(n) : __Maybe_Nothing;
334 | }
335 |
336 | function _String_fromList(chars)
337 | {
338 | return __List_toArray(chars).join('');
339 | }
340 |
341 |
--------------------------------------------------------------------------------
/src/Result.elm:
--------------------------------------------------------------------------------
1 | module Result exposing
2 | ( Result(..)
3 | , withDefault
4 | , map, map2, map3, map4, map5
5 | , andThen
6 | , toMaybe, fromMaybe, mapError
7 | )
8 |
9 | {-| A `Result` is the result of a computation that may fail. This is a great
10 | way to manage errors in Elm.
11 |
12 | # Type and Constructors
13 | @docs Result
14 |
15 | # Mapping
16 | @docs map, map2, map3, map4, map5
17 |
18 | # Chaining
19 | @docs andThen
20 |
21 | # Handling Errors
22 | @docs withDefault, toMaybe, fromMaybe, mapError
23 | -}
24 |
25 | import Basics exposing ( Bool(..) )
26 | import Maybe exposing ( Maybe(..) )
27 |
28 |
29 | {-| A `Result` is either `Ok` meaning the computation succeeded, or it is an
30 | `Err` meaning that there was some failure.
31 | -}
32 | type Result error value
33 | = Ok value
34 | | Err error
35 |
36 |
37 | {-| If the result is `Ok` return the value, but if the result is an `Err` then
38 | return a given default value. The following examples try to parse integers.
39 |
40 | Result.withDefault 0 (Ok 123) == 123
41 | Result.withDefault 0 (Err "no") == 0
42 | -}
43 | withDefault : a -> Result x a -> a
44 | withDefault def result =
45 | case result of
46 | Ok a ->
47 | a
48 |
49 | Err _ ->
50 | def
51 |
52 |
53 | {-| Apply a function to a result. If the result is `Ok`, it will be converted.
54 | If the result is an `Err`, the same error value will propagate through.
55 |
56 | map sqrt (Ok 4.0) == Ok 2.0
57 | map sqrt (Err "bad input") == Err "bad input"
58 | -}
59 | map : (a -> value) -> Result x a -> Result x value
60 | map func ra =
61 | case ra of
62 | Ok a ->
63 | Ok (func a)
64 |
65 | Err e ->
66 | Err e
67 |
68 |
69 | {-| Apply a function if both results are `Ok`. If not, the first `Err` will
70 | propagate through.
71 |
72 | map2 max (Ok 42) (Ok 13) == Ok 42
73 | map2 max (Err "x") (Ok 13) == Err "x"
74 | map2 max (Ok 42) (Err "y") == Err "y"
75 | map2 max (Err "x") (Err "y") == Err "x"
76 |
77 | This can be useful if you have two computations that may fail, and you want
78 | to put them together quickly.
79 | -}
80 | map2 : (a -> b -> value) -> Result x a -> Result x b -> Result x value
81 | map2 func ra rb =
82 | case ra of
83 | Err x ->
84 | Err x
85 |
86 | Ok a ->
87 | case rb of
88 | Err x ->
89 | Err x
90 |
91 | Ok b ->
92 | Ok (func a b)
93 |
94 |
95 | {-|-}
96 | map3 : (a -> b -> c -> value) -> Result x a -> Result x b -> Result x c -> Result x value
97 | map3 func ra rb rc =
98 | case ra of
99 | Err x ->
100 | Err x
101 |
102 | Ok a ->
103 | case rb of
104 | Err x ->
105 | Err x
106 |
107 | Ok b ->
108 | case rc of
109 | Err x ->
110 | Err x
111 |
112 | Ok c ->
113 | Ok (func a b c)
114 |
115 |
116 | {-|-}
117 | map4 : (a -> b -> c -> d -> value) -> Result x a -> Result x b -> Result x c -> Result x d -> Result x value
118 | map4 func ra rb rc rd =
119 | case ra of
120 | Err x ->
121 | Err x
122 |
123 | Ok a ->
124 | case rb of
125 | Err x ->
126 | Err x
127 |
128 | Ok b ->
129 | case rc of
130 | Err x ->
131 | Err x
132 |
133 | Ok c ->
134 | case rd of
135 | Err x ->
136 | Err x
137 |
138 | Ok d ->
139 | Ok (func a b c d)
140 |
141 |
142 | {-|-}
143 | map5 : (a -> b -> c -> d -> e -> value) -> Result x a -> Result x b -> Result x c -> Result x d -> Result x e -> Result x value
144 | map5 func ra rb rc rd re =
145 | case ra of
146 | Err x ->
147 | Err x
148 |
149 | Ok a ->
150 | case rb of
151 | Err x ->
152 | Err x
153 |
154 | Ok b ->
155 | case rc of
156 | Err x ->
157 | Err x
158 |
159 | Ok c ->
160 | case rd of
161 | Err x ->
162 | Err x
163 |
164 | Ok d ->
165 | case re of
166 | Err x ->
167 | Err x
168 |
169 | Ok e ->
170 | Ok (func a b c d e)
171 |
172 |
173 | {-| Chain together a sequence of computations that may fail. It is helpful
174 | to see its definition:
175 |
176 | andThen : (a -> Result e b) -> Result e a -> Result e b
177 | andThen callback result =
178 | case result of
179 | Ok value -> callback value
180 | Err msg -> Err msg
181 |
182 | This means we only continue with the callback if things are going well. For
183 | example, say you need to use (`toInt : String -> Result String Int`) to parse
184 | a month and make sure it is between 1 and 12:
185 |
186 | toValidMonth : Int -> Result String Int
187 | toValidMonth month =
188 | if month >= 1 && month <= 12
189 | then Ok month
190 | else Err "months must be between 1 and 12"
191 |
192 | toMonth : String -> Result String Int
193 | toMonth rawString =
194 | toInt rawString
195 | |> andThen toValidMonth
196 |
197 | -- toMonth "4" == Ok 4
198 | -- toMonth "9" == Ok 9
199 | -- toMonth "a" == Err "cannot parse to an Int"
200 | -- toMonth "0" == Err "months must be between 1 and 12"
201 |
202 | This allows us to come out of a chain of operations with quite a specific error
203 | message. It is often best to create a custom type that explicitly represents
204 | the exact ways your computation may fail. This way it is easy to handle in your
205 | code.
206 | -}
207 | andThen : (a -> Result x b) -> Result x a -> Result x b
208 | andThen callback result =
209 | case result of
210 | Ok value ->
211 | callback value
212 |
213 | Err msg ->
214 | Err msg
215 |
216 |
217 | {-| Transform an `Err` value. For example, say the errors we get have too much
218 | information:
219 |
220 | parseInt : String -> Result ParseError Int
221 |
222 | type alias ParseError =
223 | { message : String
224 | , code : Int
225 | , position : (Int,Int)
226 | }
227 |
228 | mapError .message (parseInt "123") == Ok 123
229 | mapError .message (parseInt "abc") == Err "char 'a' is not a number"
230 | -}
231 | mapError : (x -> y) -> Result x a -> Result y a
232 | mapError f result =
233 | case result of
234 | Ok v ->
235 | Ok v
236 |
237 | Err e ->
238 | Err (f e)
239 |
240 |
241 | {-| Convert to a simpler `Maybe` if the actual error message is not needed or
242 | you need to interact with some code that primarily uses maybes.
243 |
244 | parseInt : String -> Result ParseError Int
245 |
246 | maybeParseInt : String -> Maybe Int
247 | maybeParseInt string =
248 | toMaybe (parseInt string)
249 | -}
250 | toMaybe : Result x a -> Maybe a
251 | toMaybe result =
252 | case result of
253 | Ok v -> Just v
254 | Err _ -> Nothing
255 |
256 |
257 | {-| Convert from a simple `Maybe` to interact with some code that primarily
258 | uses `Results`.
259 |
260 | parseInt : String -> Maybe Int
261 |
262 | resultParseInt : String -> Result String Int
263 | resultParseInt string =
264 | fromMaybe ("error parsing string: " ++ toString string) (parseInt string)
265 | -}
266 | fromMaybe : x -> Maybe a -> Result x a
267 | fromMaybe err maybe =
268 | case maybe of
269 | Just v -> Ok v
270 | Nothing -> Err err
271 |
272 |
273 |
274 | -- FOR INTERNAL USE ONLY
275 | --
276 | -- Use `case` expressions for this in Elm code!
277 |
278 |
279 | isOk : Result x a -> Bool
280 | isOk result =
281 | case result of
282 | Ok _ ->
283 | True
284 |
285 | Err _ ->
286 | False
287 |
--------------------------------------------------------------------------------
/tests/Test/List.elm:
--------------------------------------------------------------------------------
1 | module Test.List exposing (tests)
2 |
3 | import Test exposing (..)
4 | import Expect
5 | import Basics exposing (..)
6 | import Maybe exposing (Maybe(Nothing, Just))
7 | import List exposing (..)
8 |
9 |
10 | tests : Test
11 | tests =
12 | describe "List Tests"
13 | [ testListOfN 0
14 | , testListOfN 1
15 | , testListOfN 2
16 | , testListOfN 5000
17 | ]
18 |
19 |
20 | testListOfN : Int -> Test
21 | testListOfN n =
22 | let
23 | xs =
24 | List.range 1 n
25 |
26 | xsOpp =
27 | List.range -n -1
28 |
29 | xsNeg =
30 | foldl (::) [] xsOpp
31 |
32 | -- assume foldl and (::) work
33 | zs =
34 | List.range 0 n
35 |
36 | sumSeq k =
37 | k * (k + 1) // 2
38 |
39 | xsSum =
40 | sumSeq n
41 |
42 | mid =
43 | n // 2
44 | in
45 | describe (toString n ++ " elements")
46 | [ describe "foldl"
47 | [ test "order" <| \() -> Expect.equal (n) (foldl (\x acc -> x) 0 xs)
48 | , test "total" <| \() -> Expect.equal (xsSum) (foldl (+) 0 xs)
49 | ]
50 | , describe "foldr"
51 | [ test "order" <| \() -> Expect.equal (min 1 n) (foldr (\x acc -> x) 0 xs)
52 | , test "total" <| \() -> Expect.equal (xsSum) (foldl (+) 0 xs)
53 | ]
54 | , describe "map"
55 | [ test "identity" <| \() -> Expect.equal (xs) (map identity xs)
56 | , test "linear" <| \() -> Expect.equal (List.range 2 (n + 1)) (map ((+) 1) xs)
57 | ]
58 | , test "isEmpty" <| \() -> Expect.equal (n == 0) (isEmpty xs)
59 | , test "length" <| \() -> Expect.equal (n) (length xs)
60 | , test "reverse" <| \() -> Expect.equal (xsOpp) (reverse xsNeg)
61 | , describe "member"
62 | [ test "positive" <| \() -> Expect.equal (True) (member n zs)
63 | , test "negative" <| \() -> Expect.equal (False) (member (n + 1) xs)
64 | ]
65 | , test "head" <|
66 | \() ->
67 | if n == 0 then
68 | Expect.equal (Nothing) (head xs)
69 | else
70 | Expect.equal (Just 1) (head xs)
71 | , describe "List.filter"
72 | [ test "none" <| \() -> Expect.equal ([]) (List.filter (\x -> x > n) xs)
73 | , test "one" <| \() -> Expect.equal ([ n ]) (List.filter (\z -> z == n) zs)
74 | , test "all" <| \() -> Expect.equal (xs) (List.filter (\x -> x <= n) xs)
75 | ]
76 | , describe "take"
77 | [ test "none" <| \() -> Expect.equal ([]) (take 0 xs)
78 | , test "some" <| \() -> Expect.equal (List.range 0 (n - 1)) (take n zs)
79 | , test "all" <| \() -> Expect.equal (xs) (take n xs)
80 | , test "all+" <| \() -> Expect.equal (xs) (take (n + 1) xs)
81 | ]
82 | , describe "drop"
83 | [ test "none" <| \() -> Expect.equal (xs) (drop 0 xs)
84 | , test "some" <| \() -> Expect.equal ([ n ]) (drop n zs)
85 | , test "all" <| \() -> Expect.equal ([]) (drop n xs)
86 | , test "all+" <| \() -> Expect.equal ([]) (drop (n + 1) xs)
87 | ]
88 | , test "repeat" <| \() -> Expect.equal (map (\x -> -1) xs) (repeat n -1)
89 | , test "append" <| \() -> Expect.equal (xsSum * 2) (append xs xs |> foldl (+) 0)
90 | , test "(::)" <| \() -> Expect.equal (append [ -1 ] xs) (-1 :: xs)
91 | , test "List.concat" <| \() -> Expect.equal (append xs (append zs xs)) (List.concat [ xs, zs, xs ])
92 | , test "intersperse" <|
93 | \() ->
94 | Expect.equal
95 | ( min -(n - 1) 0, xsSum )
96 | (intersperse -1 xs |> foldl (\x ( c1, c2 ) -> ( c2, c1 + x )) ( 0, 0 ))
97 | , describe "partition"
98 | [ test "left" <| \() -> Expect.equal ( xs, [] ) (partition (\x -> x > 0) xs)
99 | , test "right" <| \() -> Expect.equal ( [], xs ) (partition (\x -> x < 0) xs)
100 | , test "split" <| \() -> Expect.equal ( List.range (mid + 1) n, List.range 1 mid ) (partition (\x -> x > mid) xs)
101 | ]
102 | , describe "map2"
103 | [ test "same length" <| \() -> Expect.equal (map ((*) 2) xs) (map2 (+) xs xs)
104 | , test "long first" <| \() -> Expect.equal (map (\x -> x * 2 - 1) xs) (map2 (+) zs xs)
105 | , test "short first" <| \() -> Expect.equal (map (\x -> x * 2 - 1) xs) (map2 (+) xs zs)
106 | ]
107 | , test "unzip" <| \() -> Expect.equal ( xsNeg, xs ) (map (\x -> ( -x, x )) xs |> unzip)
108 | , describe "filterMap"
109 | [ test "none" <| \() -> Expect.equal ([]) (filterMap (\x -> Nothing) xs)
110 | , test "all" <| \() -> Expect.equal (xsNeg) (filterMap (\x -> Just -x) xs)
111 | , let
112 | halve x =
113 | if x % 2 == 0 then
114 | Just (x // 2)
115 | else
116 | Nothing
117 | in
118 | test "some" <| \() -> Expect.equal (List.range 1 mid) (filterMap halve xs)
119 | ]
120 | , describe "concatMap"
121 | [ test "none" <| \() -> Expect.equal ([]) (concatMap (\x -> []) xs)
122 | , test "all" <| \() -> Expect.equal (xsNeg) (concatMap (\x -> [ -x ]) xs)
123 | ]
124 | , test "indexedMap" <| \() -> Expect.equal (map2 (,) zs xsNeg) (indexedMap (\i x -> ( i, -x )) xs)
125 | , test "sum" <| \() -> Expect.equal (xsSum) (sum xs)
126 | , test "product" <| \() -> Expect.equal (0) (product zs)
127 | , test "maximum" <|
128 | \() ->
129 | if n == 0 then
130 | Expect.equal (Nothing) (maximum xs)
131 | else
132 | Expect.equal (Just n) (maximum xs)
133 | , test "minimum" <|
134 | \() ->
135 | if n == 0 then
136 | Expect.equal (Nothing) (minimum xs)
137 | else
138 | Expect.equal (Just 1) (minimum xs)
139 | , describe "all"
140 | [ test "false" <| \() -> Expect.equal (False) (all (\z -> z < n) zs)
141 | , test "true" <| \() -> Expect.equal (True) (all (\x -> x <= n) xs)
142 | ]
143 | , describe "any"
144 | [ test "false" <| \() -> Expect.equal (False) (any (\x -> x > n) xs)
145 | , test "true" <| \() -> Expect.equal (True) (any (\z -> z >= n) zs)
146 | ]
147 | , describe "sort"
148 | [ test "sorted" <| \() -> Expect.equal (xs) (sort xs)
149 | , test "unsorted" <| \() -> Expect.equal (xsOpp) (sort xsNeg)
150 | ]
151 | , describe "sortBy"
152 | [ test "sorted" <| \() -> Expect.equal (xsNeg) (sortBy negate xsNeg)
153 | , test "unsorted" <| \() -> Expect.equal (xsNeg) (sortBy negate xsOpp)
154 | ]
155 | , describe "sortWith"
156 | [ test "sorted" <| \() -> Expect.equal (xsNeg) (sortWith (flip compare) xsNeg)
157 | , test "unsorted" <| \() -> Expect.equal (xsNeg) (sortWith (flip compare) xsOpp)
158 | ]
159 | , test "scanl" <| \() -> Expect.equal (0 :: map sumSeq xs) (scanl (+) 0 xs)
160 | ]
161 |
--------------------------------------------------------------------------------
/src/Task.elm:
--------------------------------------------------------------------------------
1 | effect module Task where { command = MyCmd } exposing
2 | ( Task
3 | , succeed, fail
4 | , map, map2, map3, map4, map5
5 | , sequence
6 | , andThen
7 | , onError, mapError
8 | , perform, attempt
9 | )
10 |
11 | {-| Tasks make it easy to describe asynchronous operations that may fail, like
12 | HTTP requests or writing to a database.
13 |
14 | # Tasks
15 | @docs Task, perform, attempt
16 |
17 | # Chains
18 | @docs andThen, succeed, fail, sequence
19 |
20 | # Maps
21 | @docs map, map2, map3, map4, map5
22 |
23 | # Errors
24 | @docs onError, mapError
25 |
26 | -}
27 |
28 | import Basics exposing (Never, (|>), (<<))
29 | import Elm.Kernel.Scheduler
30 | import List exposing ((::))
31 | import Maybe exposing (Maybe(..))
32 | import Platform
33 | import Platform.Cmd exposing (Cmd)
34 | import Result exposing (Result(..))
35 |
36 |
37 |
38 | {-| Here are some common tasks:
39 |
40 | - [`Time.now : Task x Posix`][now]
41 | - [`Browser.focus : String -> Task DomError ()`][focus]
42 | - [`Process.sleep : Float -> Task x ()`][sleep]
43 |
44 | [now]: /packages/elm-lang/time/latest/Time#now
45 | [focus]: /packages/elm-lang/browser/latest/Browser#focus
46 | [sleep]: /packages/elm-lang/core/latest/Process#sleep
47 |
48 | In each case we have a `Task` that will resolve successfully with an `a` value
49 | or unsuccessfully with an `x` value. So `Browser.focus` we may fail with a
50 | `DomError` if the given ID does not exist. Whereas `Time.now` never fails so
51 | I cannot be more specific than `x`. No such value will ever exist! Instead it
52 | always succeeds with the current POSIX time.
53 |
54 | More generally a task is a _description_ of what you need to do. Like a todo
55 | list. Or like a grocery list. Or like GitHub issues. So saying "the task is
56 | to tell me the current POSIX time" does not complete the task! You need
57 | [`perform`](#perform) tasks or [`attempt`](#attempt) tasks.
58 | -}
59 | type alias Task x a =
60 | Platform.Task x a
61 |
62 |
63 |
64 | -- BASICS
65 |
66 |
67 | {-| A task that succeeds immediately when run. It is usually used with
68 | [`andThen`](#andThen). You can use it like `map` if you want:
69 |
70 | import Time -- elm install elm-lang/time
71 |
72 | timeInMillis : Task x Int
73 | timeInMillis =
74 | Time.now
75 | |> andThen (\t -> succeed (Time.posixToMillis t))
76 |
77 | -}
78 | succeed : a -> Task x a
79 | succeed =
80 | Elm.Kernel.Scheduler.succeed
81 |
82 |
83 | {-| A task that fails immediately when run. Like with `succeed`, this can be
84 | used with `andThen` to check on the outcome of another task.
85 |
86 | type Error = NotFound
87 |
88 | notFound : Task Error a
89 | notFound =
90 | fail NotFound
91 | -}
92 | fail : x -> Task x a
93 | fail =
94 | Elm.Kernel.Scheduler.fail
95 |
96 |
97 |
98 | -- MAPPING
99 |
100 |
101 | {-| Transform a task. Maybe you want to use [`elm-lang/time`][time] to figure
102 | out what time it will be in one hour:
103 |
104 | import Task exposing (Task)
105 | import Time -- elm install elm-lang/time
106 |
107 | timeInOneHour : Task x Time.Posix
108 | timeInOneHour =
109 | Task.map addAnHour Time.now
110 |
111 | addAnHour : Time.Posix -> Time.Posix
112 | addAnHour time =
113 | Time.millisToPosix (Time.posixToMillis time + 60 * 60 * 1000)
114 |
115 | [time]: /packages/elm-lang/time/latest/
116 | -}
117 | map : (a -> b) -> Task x a -> Task x b
118 | map func taskA =
119 | taskA
120 | |> andThen (\a -> succeed (func a))
121 |
122 |
123 | {-| Put the results of two tasks together. For example, if we wanted to know
124 | the current month, we could use [`elm-lang/time`][time] to ask:
125 |
126 | import Task exposing (Task)
127 | import Time -- elm install elm-lang/time
128 |
129 | getMonth : Task x Int
130 | getMonth =
131 | Task.map2 Time.toMonth Time.here Time.now
132 |
133 | **Note:** Say we were doing HTTP requests instead. `map2` does each task in
134 | order, so it would try the first request and only continue after it succeeds.
135 | If it fails, the whole thing fails!
136 |
137 | [time]: /packages/elm-lang/time/latest/
138 | -}
139 | map2 : (a -> b -> result) -> Task x a -> Task x b -> Task x result
140 | map2 func taskA taskB =
141 | taskA
142 | |> andThen (\a -> taskB
143 | |> andThen (\b -> succeed (func a b)))
144 |
145 |
146 | {-|-}
147 | map3 : (a -> b -> c -> result) -> Task x a -> Task x b -> Task x c -> Task x result
148 | map3 func taskA taskB taskC =
149 | taskA
150 | |> andThen (\a -> taskB
151 | |> andThen (\b -> taskC
152 | |> andThen (\c -> succeed (func a b c))))
153 |
154 |
155 | {-|-}
156 | map4 : (a -> b -> c -> d -> result) -> Task x a -> Task x b -> Task x c -> Task x d -> Task x result
157 | map4 func taskA taskB taskC taskD =
158 | taskA
159 | |> andThen (\a -> taskB
160 | |> andThen (\b -> taskC
161 | |> andThen (\c -> taskD
162 | |> andThen (\d -> succeed (func a b c d)))))
163 |
164 |
165 | {-|-}
166 | map5 : (a -> b -> c -> d -> e -> result) -> Task x a -> Task x b -> Task x c -> Task x d -> Task x e -> Task x result
167 | map5 func taskA taskB taskC taskD taskE =
168 | taskA
169 | |> andThen (\a -> taskB
170 | |> andThen (\b -> taskC
171 | |> andThen (\c -> taskD
172 | |> andThen (\d -> taskE
173 | |> andThen (\e -> succeed (func a b c d e))))))
174 |
175 |
176 | {-| Start with a list of tasks, and turn them into a single task that returns a
177 | list. The tasks will be run in order one-by-one and if any task fails the whole
178 | sequence fails.
179 |
180 | sequence [ succeed 1, succeed 2 ] == succeed [ 1, 2 ]
181 |
182 | -}
183 | sequence : List (Task x a) -> Task x (List a)
184 | sequence tasks =
185 | List.foldr (map2 (::)) (succeed []) tasks
186 |
187 |
188 |
189 | -- CHAINING
190 |
191 |
192 | {-| Chain together a task and a callback. The first task will run, and if it is
193 | successful, you give the result to the callback resulting in another task. This
194 | task then gets run. We could use this to make a task that resolves an hour from
195 | now:
196 |
197 | import Time -- elm install elm-lang/time
198 | import Process
199 |
200 | timeInOneHour : Task x Time.Posix
201 | timeInOneHour =
202 | Process.sleep (60 * 60 * 1000)
203 | |> andThen (\_ -> Time.now)
204 |
205 | First the process sleeps for an hour **and then** it tells us what time it is.
206 | -}
207 | andThen : (a -> Task x b) -> Task x a -> Task x b
208 | andThen =
209 | Elm.Kernel.Scheduler.andThen
210 |
211 |
212 |
213 | -- ERRORS
214 |
215 |
216 | {-| Recover from a failure in a task. If the given task fails, we use the
217 | callback to recover.
218 |
219 | fail "file not found"
220 | |> onError (\msg -> succeed 42)
221 | -- succeed 42
222 |
223 | succeed 9
224 | |> onError (\msg -> succeed 42)
225 | -- succeed 9
226 | -}
227 | onError : (x -> Task y a) -> Task x a -> Task y a
228 | onError =
229 | Elm.Kernel.Scheduler.onError
230 |
231 |
232 | {-| Transform the error value. This can be useful if you need a bunch of error
233 | types to match up.
234 |
235 | type Error
236 | = Http Http.Error
237 | | WebGL WebGL.Error
238 |
239 | getResources : Task Error Resource
240 | getResources =
241 | sequence
242 | [ mapError Http serverTask
243 | , mapError WebGL textureTask
244 | ]
245 | -}
246 | mapError : (x -> y) -> Task x a -> Task y a
247 | mapError convert task =
248 | task
249 | |> onError (fail << convert)
250 |
251 |
252 |
253 | -- COMMANDS
254 |
255 |
256 | type MyCmd msg =
257 | Perform (Task Never msg)
258 |
259 |
260 | {-| Like I was saying in the [`Task`](#Task) documentation, just having a
261 | `Task` does not mean it is done. We must command Elm to `perform` the task:
262 |
263 | import Time -- elm install elm-lang/time
264 | import Task
265 |
266 | type Msg
267 | = Click
268 | | Search String
269 | | NewTime Time.Posix
270 |
271 | getNewTime : Cmd Msg
272 | getNewTime =
273 | Task.perform NewTime Time.now
274 |
275 | If you have worked through [`guide.elm-lang.org`][guide] (highly recommended!)
276 | you will recognize `Cmd` from the section on The Elm Architecture. So we have
277 | changed a task like "make delicious lasagna" into a command like "Hey Elm, make
278 | delicious lasagna and give it to my `update` function as a `Msg` value."
279 |
280 | [guide]: https://guide.elm-lang.org/
281 | -}
282 | perform : (a -> msg) -> Task Never a -> Cmd msg
283 | perform toMessage task =
284 | command (Perform (map toMessage task))
285 |
286 |
287 | {-| This is very similar to [`perform`](#perform) except it can handle failures!
288 | So we could _attempt_ to focus on a certain DOM node like this:
289 |
290 | import Browser -- elm install elm-lang/browser
291 | import Task
292 |
293 | type Msg
294 | = Click
295 | | Search String
296 | | Focus (Result Browser.DomError ())
297 |
298 | focus : Cmd Msg
299 | focus =
300 | Task.attempt Focus (Browser.focus "my-app-search-box")
301 |
302 | So the task is "focus on this DOM node" and we are turning it into the command
303 | "Hey Elm, attempt to focus on this DOM node and give me a `Msg` about whether
304 | you succeeded or failed."
305 |
306 | **Note:** Definitely work through [`guide.elm-lang.org`][guide] to get a
307 | feeling for how commands fit into The Elm Architecture.
308 |
309 | [guide]: https://guide.elm-lang.org/
310 | -}
311 | attempt : (Result x a -> msg) -> Task x a -> Cmd msg
312 | attempt resultToMessage task =
313 | command (Perform (
314 | task
315 | |> andThen (succeed << resultToMessage << Ok)
316 | |> onError (succeed << resultToMessage << Err)
317 | ))
318 |
319 |
320 | cmdMap : (a -> b) -> MyCmd a -> MyCmd b
321 | cmdMap tagger (Perform task) =
322 | Perform (map tagger task)
323 |
324 |
325 |
326 | -- MANAGER
327 |
328 |
329 | init : Task Never ()
330 | init =
331 | succeed ()
332 |
333 |
334 | onEffects : Platform.Router msg Never -> List (MyCmd msg) -> () -> Task Never ()
335 | onEffects router commands state =
336 | map
337 | (\_ -> ())
338 | (sequence (List.map (spawnCmd router) commands))
339 |
340 |
341 | onSelfMsg : Platform.Router msg Never -> Never -> () -> Task Never ()
342 | onSelfMsg _ _ _ =
343 | succeed ()
344 |
345 |
346 | spawnCmd : Platform.Router msg Never -> MyCmd msg -> Task x ()
347 | spawnCmd router (Perform task) =
348 | Elm.Kernel.Scheduler.spawn (
349 | task
350 | |> andThen (Platform.sendToApp router)
351 | )
352 |
--------------------------------------------------------------------------------
/tests/Test/Set.elm:
--------------------------------------------------------------------------------
1 | module Test.Set exposing (tests)
2 |
3 | import Basics exposing (..)
4 | import Set exposing (Set)
5 | import List exposing ((::))
6 | import Test exposing (..)
7 | import Expect
8 |
9 |
10 | tests : Test
11 | tests =
12 | describe "Set Tests"
13 | [ describe "empty" emptyTests
14 | , describe "singleton" singletonTests
15 | , describe "insert" insertTests
16 | , describe "remove" removeTests
17 | , describe "isEmpty" isEmptyTests
18 | , describe "member" memberTests
19 | , describe "size" sizeTests
20 | , describe "foldl" foldlTests
21 | , describe "foldr" foldrTests
22 | , describe "map" mapTests
23 | , describe "filter" filterTests
24 | , describe "partition" partitionTests
25 | , describe "union" unionTests
26 | , describe "intersect" intersectTests
27 | , describe "diff" diffTests
28 | , describe "toList" toListTests
29 | , describe "fromList" fromListTests
30 | ]
31 |
32 |
33 |
34 | -- HELPERS
35 |
36 |
37 | set42 : Set Int
38 | set42 =
39 | Set.singleton 42
40 |
41 |
42 | set1To100 : Set Int
43 | set1To100 =
44 | Set.fromList (List.range 1 100)
45 |
46 |
47 | set1To50 : Set Int
48 | set1To50 =
49 | Set.fromList (List.range 1 50)
50 |
51 |
52 | set51To100 : Set Int
53 | set51To100 =
54 | Set.fromList (List.range 51 100)
55 |
56 |
57 | set51To150 : Set Int
58 | set51To150 =
59 | Set.fromList (List.range 51 150)
60 |
61 |
62 | isLessThan51 : Int -> Bool
63 | isLessThan51 n =
64 | n < 51
65 |
66 |
67 |
68 | -- TESTS
69 |
70 |
71 | emptyTests : List Test
72 | emptyTests =
73 | [ test "returns an empty set" <|
74 | \() -> Expect.equal 0 (Set.size Set.empty)
75 | ]
76 |
77 |
78 | singletonTests : List Test
79 | singletonTests =
80 | [ test "returns set with one element" <|
81 | \() -> Expect.equal 1 (Set.size (Set.singleton 1))
82 | , test "contains given element" <|
83 | \() -> Expect.equal True (Set.member 1 (Set.singleton 1))
84 | ]
85 |
86 |
87 | insertTests : List Test
88 | insertTests =
89 | [ test "adds new element to empty set" <|
90 | \() -> Expect.equal set42 (Set.insert 42 Set.empty)
91 | , test "adds new element to a set of 100" <|
92 | \() -> Expect.equal (Set.fromList (List.range 1 101)) (Set.insert 101 set1To100)
93 | , test "leaves singleton set intact if it contains given element" <|
94 | \() -> Expect.equal set42 (Set.insert 42 set42)
95 | , test "leaves set of 100 intact if it contains given element" <|
96 | \() -> Expect.equal set1To100 (Set.insert 42 set1To100)
97 | ]
98 |
99 |
100 | removeTests : List Test
101 | removeTests =
102 | [ test "removes element from singleton set" <|
103 | \() -> Expect.equal Set.empty (Set.remove 42 set42)
104 | , test "removes element from set of 100" <|
105 | \() -> Expect.equal (Set.fromList (List.range 1 99)) (Set.remove 100 set1To100)
106 | , test "leaves singleton set intact if it doesn't contain given element" <|
107 | \() -> Expect.equal set42 (Set.remove -1 set42)
108 | , test "leaves set of 100 intact if it doesn't contain given element" <|
109 | \() -> Expect.equal set1To100 (Set.remove -1 set1To100)
110 | ]
111 |
112 |
113 | isEmptyTests : List Test
114 | isEmptyTests =
115 | [ test "returns True for empty set" <|
116 | \() -> Expect.equal True (Set.isEmpty Set.empty)
117 | , test "returns False for singleton set" <|
118 | \() -> Expect.equal False (Set.isEmpty set42)
119 | , test "returns False for set of 100" <|
120 | \() -> Expect.equal False (Set.isEmpty set1To100)
121 | ]
122 |
123 |
124 | memberTests : List Test
125 | memberTests =
126 | [ test "returns True when given element inside singleton set" <|
127 | \() -> Expect.equal True (Set.member 42 set42)
128 | , test "returns True when given element inside set of 100" <|
129 | \() -> Expect.equal True (Set.member 42 set1To100)
130 | , test "returns False for element not in singleton" <|
131 | \() -> Expect.equal False (Set.member -1 set42)
132 | , test "returns False for element not in set of 100" <|
133 | \() -> Expect.equal False (Set.member -1 set1To100)
134 | ]
135 |
136 |
137 | sizeTests : List Test
138 | sizeTests =
139 | [ test "returns 0 for empty set" <|
140 | \() -> Expect.equal 0 (Set.size Set.empty)
141 | , test "returns 1 for singleton set" <|
142 | \() -> Expect.equal 1 (Set.size set42)
143 | , test "returns 100 for set of 100" <|
144 | \() -> Expect.equal 100 (Set.size set1To100)
145 | ]
146 |
147 |
148 | foldlTests : List Test
149 | foldlTests =
150 | [ test "with insert and empty set acts as identity function" <|
151 | \() -> Expect.equal set1To100 (Set.foldl Set.insert Set.empty set1To100)
152 | , test "with counter and zero acts as size function" <|
153 | \() -> Expect.equal 100 (Set.foldl (\_ count -> count + 1) 0 set1To100)
154 | , test "folds set elements from lowest to highest" <|
155 | \() -> Expect.equal [ 3, 2, 1 ] (Set.foldl (\n ns -> n :: ns) [] (Set.fromList [ 2, 1, 3 ]))
156 | ]
157 |
158 |
159 | foldrTests : List Test
160 | foldrTests =
161 | [ test "with insert and empty set acts as identity function" <|
162 | \() -> Expect.equal set1To100 (Set.foldr Set.insert Set.empty set1To100)
163 | , test "with counter and zero acts as size function" <|
164 | \() -> Expect.equal 100 (Set.foldr (\_ count -> count + 1) 0 set1To100)
165 | , test "folds set elements from highest to lowest" <|
166 | \() -> Expect.equal [ 1, 2, 3 ] (Set.foldr (\n ns -> n :: ns) [] (Set.fromList [ 2, 1, 3 ]))
167 | ]
168 |
169 |
170 | mapTests : List Test
171 | mapTests =
172 | [ test "applies given function to singleton element" <|
173 | \() -> Expect.equal (Set.singleton 43) (Set.map ((+) 1) set42)
174 | , test "applies given function to each element" <|
175 | \() -> Expect.equal (Set.fromList (List.range -100 -1)) (Set.map negate set1To100)
176 | ]
177 |
178 |
179 | filterTests : List Test
180 | filterTests =
181 | [ test "with always True doesn't change anything" <|
182 | \() -> Expect.equal set1To100 (Set.filter (always True) set1To100)
183 | , test "with always False returns empty set" <|
184 | \() -> Expect.equal Set.empty (Set.filter (always False) set1To100)
185 | , test "simple filter" <|
186 | \() -> Expect.equal set1To50 (Set.filter isLessThan51 set1To100)
187 | ]
188 |
189 |
190 | partitionTests : List Test
191 | partitionTests =
192 | [ test "of empty set returns two empty sets" <|
193 | \() -> Expect.equal ( Set.empty, Set.empty ) (Set.partition isLessThan51 Set.empty)
194 | , test "simple partition" <|
195 | \() -> Expect.equal ( set1To50, set51To100 ) (Set.partition isLessThan51 set1To100)
196 | ]
197 |
198 |
199 | unionTests : List Test
200 | unionTests =
201 | [ test "with empty set doesn't change anything" <|
202 | \() -> Expect.equal set42 (Set.union set42 Set.empty)
203 | , test "with itself doesn't change anything" <|
204 | \() -> Expect.equal set1To100 (Set.union set1To100 set1To100)
205 | , test "with subset doesn't change anything" <|
206 | \() -> Expect.equal set1To100 (Set.union set1To100 set42)
207 | , test "with superset returns superset" <|
208 | \() -> Expect.equal set1To100 (Set.union set42 set1To100)
209 | , test "contains elements of both singletons" <|
210 | \() -> Expect.equal (Set.insert 1 set42) (Set.union set42 (Set.singleton 1))
211 | , test "consists of elements from either set" <|
212 | \() ->
213 | Set.union set1To100 set51To150
214 | |> Expect.equal (Set.fromList (List.range 1 150))
215 | ]
216 |
217 |
218 | intersectTests : List Test
219 | intersectTests =
220 | [ test "with empty set returns empty set" <|
221 | \() -> Expect.equal Set.empty (Set.intersect set42 Set.empty)
222 | , test "with itself doesn't change anything" <|
223 | \() -> Expect.equal set1To100 (Set.intersect set1To100 set1To100)
224 | , test "with subset returns subset" <|
225 | \() -> Expect.equal set42 (Set.intersect set1To100 set42)
226 | , test "with superset doesn't change anything" <|
227 | \() -> Expect.equal set42 (Set.intersect set42 set1To100)
228 | , test "returns empty set given disjunctive sets" <|
229 | \() -> Expect.equal Set.empty (Set.intersect set42 (Set.singleton 1))
230 | , test "consists of common elements only" <|
231 | \() ->
232 | Set.intersect set1To100 set51To150
233 | |> Expect.equal set51To100
234 | ]
235 |
236 |
237 | diffTests : List Test
238 | diffTests =
239 | [ test "with empty set doesn't change anything" <|
240 | \() -> Expect.equal set42 (Set.diff set42 Set.empty)
241 | , test "with itself returns empty set" <|
242 | \() -> Expect.equal Set.empty (Set.diff set1To100 set1To100)
243 | , test "with subset returns set without subset elements" <|
244 | \() -> Expect.equal (Set.remove 42 set1To100) (Set.diff set1To100 set42)
245 | , test "with superset returns empty set" <|
246 | \() -> Expect.equal Set.empty (Set.diff set42 set1To100)
247 | , test "doesn't change anything given disjunctive sets" <|
248 | \() -> Expect.equal set42 (Set.diff set42 (Set.singleton 1))
249 | , test "only keeps values that don't appear in the second set" <|
250 | \() ->
251 | Set.diff set1To100 set51To150
252 | |> Expect.equal set1To50
253 | ]
254 |
255 |
256 | toListTests : List Test
257 | toListTests =
258 | [ test "returns empty list for empty set" <|
259 | \() -> Expect.equal [] (Set.toList Set.empty)
260 | , test "returns singleton list for singleton set" <|
261 | \() -> Expect.equal [ 42 ] (Set.toList set42)
262 | , test "returns sorted list of set elements" <|
263 | \() -> Expect.equal (List.range 1 100) (Set.toList set1To100)
264 | ]
265 |
266 |
267 | fromListTests : List Test
268 | fromListTests =
269 | [ test "returns empty set for empty list" <|
270 | \() -> Expect.equal Set.empty (Set.fromList [])
271 | , test "returns singleton set for singleton list" <|
272 | \() -> Expect.equal set42 (Set.fromList [ 42 ])
273 | , test "returns set with unique list elements" <|
274 | \() -> Expect.equal set1To100 (Set.fromList (1 :: (List.range 1 100)))
275 | ]
276 |
--------------------------------------------------------------------------------
/src/Elm/Kernel/Platform.js:
--------------------------------------------------------------------------------
1 | /*
2 |
3 | import Elm.Kernel.Error exposing (throw)
4 | import Elm.Kernel.Json exposing (run, wrap, unwrap)
5 | import Elm.Kernel.List exposing (Cons, Nil)
6 | import Elm.Kernel.Scheduler exposing (andThen, binding, rawSend, rawSpawn, receive, send, succeed)
7 | import Elm.Kernel.Utils exposing (Tuple0)
8 | import Result exposing (isOk)
9 |
10 | */
11 |
12 |
13 |
14 | // PROGRAMS
15 |
16 |
17 | var _Platform_worker = F4(function(impl, flagDecoder, debugMetadata, object)
18 | {
19 | object['worker'] = function(flags)
20 | {
21 | return _Platform_initialize(
22 | flagDecoder,
23 | flags,
24 | impl.__$init,
25 | impl.__$update,
26 | impl.__$subscriptions,
27 | function() { return function() {} }
28 | );
29 | };
30 | return object;
31 | });
32 |
33 |
34 |
35 | // INITIALIZE A PROGRAM
36 |
37 |
38 | function _Platform_initialize(flagDecoder, flags, init, update, subscriptions, stepperBuilder)
39 | {
40 | var result = A2(__Json_run, flagDecoder, __Json_wrap(flags));
41 | __Result_isOk(result) || __Error_throw(2, result.a);
42 | var managers = {};
43 | result = init(result.a);
44 | var model = result.a;
45 | var stepper = stepperBuilder(sendToApp, model);
46 | var ports = _Platform_setupEffects(managers, sendToApp);
47 |
48 | function sendToApp(msg, viewMetadata)
49 | {
50 | result = A2(update, msg, model);
51 | stepper(model = result.a, viewMetadata);
52 | _Platform_dispatchEffects(managers, result.b, subscriptions(model));
53 | }
54 |
55 | _Platform_dispatchEffects(managers, result.b, subscriptions(model));
56 |
57 | return ports ? { ports: ports } : {};
58 | }
59 |
60 |
61 |
62 | // TRACK PRELOADS
63 | //
64 | // This is used by code in elm-lang/browser and elm-lang/http
65 | // to register any HTTP requests that are triggered by init.
66 | //
67 |
68 |
69 | var _Platform_preload;
70 |
71 |
72 | function _Platform_registerPreload(url)
73 | {
74 | _Platform_preload.add(url);
75 | }
76 |
77 |
78 |
79 | // EFFECT MANAGERS
80 |
81 |
82 | var _Platform_effectManagers = {};
83 |
84 |
85 | function _Platform_setupEffects(managers, sendToApp)
86 | {
87 | var ports;
88 |
89 | // setup all necessary effect managers
90 | for (var key in _Platform_effectManagers)
91 | {
92 | var manager = _Platform_effectManagers[key];
93 |
94 | if (manager.__portSetup)
95 | {
96 | ports = ports || {};
97 | ports[key] = manager.__portSetup(key, sendToApp);
98 | }
99 |
100 | managers[key] = _Platform_instantiateManager(manager, sendToApp);
101 | }
102 |
103 | return ports;
104 | }
105 |
106 |
107 | function _Platform_createManager(init, onEffects, onSelfMsg, cmdMap, subMap)
108 | {
109 | return {
110 | __init: init,
111 | __onEffects: onEffects,
112 | __onSelfMsg: onSelfMsg,
113 | __cmdMap: cmdMap,
114 | __subMap: subMap
115 | };
116 | }
117 |
118 |
119 | function _Platform_instantiateManager(info, sendToApp)
120 | {
121 | var router = {
122 | __sendToApp: sendToApp,
123 | __selfProcess: undefined
124 | };
125 |
126 | var onEffects = info.__onEffects;
127 | var onSelfMsg = info.__onSelfMsg;
128 | var cmdMap = info.__cmdMap;
129 | var subMap = info.__subMap;
130 |
131 | function loop(state)
132 | {
133 | return A2(__Scheduler_andThen, loop, __Scheduler_receive(function(msg)
134 | {
135 | var value = msg.a;
136 |
137 | if (msg.$ === __2_SELF)
138 | {
139 | return A3(onSelfMsg, router, value, state);
140 | }
141 |
142 | return cmdMap && subMap
143 | ? A4(onEffects, router, value.__cmds, value.__subs, state)
144 | : A3(onEffects, router, cmdMap ? value.__cmds : value.__subs, state);
145 | }));
146 | }
147 |
148 | return router.__selfProcess = __Scheduler_rawSpawn(A2(__Scheduler_andThen, loop, info.__init));
149 | }
150 |
151 |
152 |
153 | // ROUTING
154 |
155 |
156 | var _Platform_sendToApp = F2(function(router, msg)
157 | {
158 | return __Scheduler_binding(function(callback)
159 | {
160 | router.__sendToApp(msg);
161 | callback(__Scheduler_succeed(__Utils_Tuple0));
162 | });
163 | });
164 |
165 |
166 | var _Platform_sendToSelf = F2(function(router, msg)
167 | {
168 | return A2(__Scheduler_send, router.__selfProcess, {
169 | $: __2_SELF,
170 | a: msg
171 | });
172 | });
173 |
174 |
175 |
176 | // BAGS
177 |
178 |
179 | function _Platform_leaf(home)
180 | {
181 | return function(value)
182 | {
183 | return {
184 | $: __2_LEAF,
185 | __home: home,
186 | __value: value
187 | };
188 | };
189 | }
190 |
191 |
192 | function _Platform_batch(list)
193 | {
194 | return {
195 | $: __2_NODE,
196 | __bags: list
197 | };
198 | }
199 |
200 |
201 | var _Platform_map = F2(function(tagger, bag)
202 | {
203 | return {
204 | $: __2_MAP,
205 | __func: tagger,
206 | __bag: bag
207 | }
208 | });
209 |
210 |
211 |
212 | // PIPE BAGS INTO EFFECT MANAGERS
213 |
214 |
215 | function _Platform_dispatchEffects(managers, cmdBag, subBag)
216 | {
217 | var effectsDict = {};
218 | _Platform_gatherEffects(true, cmdBag, effectsDict, null);
219 | _Platform_gatherEffects(false, subBag, effectsDict, null);
220 |
221 | for (var home in managers)
222 | {
223 | __Scheduler_rawSend(managers[home], {
224 | $: 'fx',
225 | a: effectsDict[home] || { __cmds: __List_Nil, __subs: __List_Nil }
226 | });
227 | }
228 | }
229 |
230 |
231 | function _Platform_gatherEffects(isCmd, bag, effectsDict, taggers)
232 | {
233 | switch (bag.$)
234 | {
235 | case __2_LEAF:
236 | var home = bag.__home;
237 | var effect = _Platform_toEffect(isCmd, home, taggers, bag.__value);
238 | effectsDict[home] = _Platform_insert(isCmd, effect, effectsDict[home]);
239 | return;
240 |
241 | case __2_NODE:
242 | for (var list = bag.__bags; list.b; list = list.b) // WHILE_CONS
243 | {
244 | _Platform_gatherEffects(isCmd, list.a, effectsDict, taggers);
245 | }
246 | return;
247 |
248 | case __2_MAP:
249 | _Platform_gatherEffects(isCmd, bag.__bag, effectsDict, {
250 | __tagger: bag.__func,
251 | __rest: taggers
252 | });
253 | return;
254 | }
255 | }
256 |
257 |
258 | function _Platform_toEffect(isCmd, home, taggers, value)
259 | {
260 | function applyTaggers(x)
261 | {
262 | for (var temp = taggers; temp; temp = temp.__rest)
263 | {
264 | x = temp.__tagger(x);
265 | }
266 | return x;
267 | }
268 |
269 | var map = isCmd
270 | ? _Platform_effectManagers[home].__cmdMap
271 | : _Platform_effectManagers[home].__subMap;
272 |
273 | return A2(map, applyTaggers, value)
274 | }
275 |
276 |
277 | function _Platform_insert(isCmd, newEffect, effects)
278 | {
279 | effects = effects || { __cmds: __List_Nil, __subs: __List_Nil };
280 |
281 | isCmd
282 | ? (effects.__cmds = __List_Cons(newEffect, effects.__cmds))
283 | : (effects.__subs = __List_Cons(newEffect, effects.__subs));
284 |
285 | return effects;
286 | }
287 |
288 |
289 |
290 | // PORTS
291 |
292 |
293 | function _Platform_checkPortName(name)
294 | {
295 | if (_Platform_effectManagers[name])
296 | {
297 | __Error_throw(3, name)
298 | }
299 | }
300 |
301 |
302 |
303 | // OUTGOING PORTS
304 |
305 |
306 | function _Platform_outgoingPort(name, converter)
307 | {
308 | _Platform_checkPortName(name);
309 | _Platform_effectManagers[name] = {
310 | __cmdMap: _Platform_outgoingPortMap,
311 | __converter: converter,
312 | __portSetup: _Platform_setupOutgoingPort
313 | };
314 | return _Platform_leaf(name);
315 | }
316 |
317 |
318 | var _Platform_outgoingPortMap = F2(function(tagger, value) { return value; });
319 |
320 |
321 | function _Platform_setupOutgoingPort(name)
322 | {
323 | var subs = [];
324 | var converter = _Platform_effectManagers[name].__converter;
325 |
326 | // CREATE MANAGER
327 |
328 | var init = __Scheduler_succeed(null);
329 |
330 | _Platform_effectManagers[name].__init = init;
331 | _Platform_effectManagers[name].__onEffects = F3(function(router, cmdList, state)
332 | {
333 | for ( ; cmdList.b; cmdList = cmdList.b) // WHILE_CONS
334 | {
335 | // grab a separate reference to subs in case unsubscribe is called
336 | var currentSubs = subs;
337 | var value = __Json_unwrap(converter(cmdList.a));
338 | for (var i = 0; i < currentSubs.length; i++)
339 | {
340 | currentSubs[i](value);
341 | }
342 | }
343 | return init;
344 | });
345 |
346 | // PUBLIC API
347 |
348 | function subscribe(callback)
349 | {
350 | subs.push(callback);
351 | }
352 |
353 | function unsubscribe(callback)
354 | {
355 | // copy subs into a new array in case unsubscribe is called within a
356 | // subscribed callback
357 | subs = subs.slice();
358 | var index = subs.indexOf(callback);
359 | if (index >= 0)
360 | {
361 | subs.splice(index, 1);
362 | }
363 | }
364 |
365 | return {
366 | subscribe: subscribe,
367 | unsubscribe: unsubscribe
368 | };
369 | }
370 |
371 |
372 |
373 | // INCOMING PORTS
374 |
375 |
376 | function _Platform_incomingPort(name, converter)
377 | {
378 | _Platform_checkPortName(name);
379 | _Platform_effectManagers[name] = {
380 | __subMap: _Platform_incomingPortMap,
381 | __converter: converter,
382 | __portSetup: _Platform_setupIncomingPort
383 | };
384 | return _Platform_leaf(name);
385 | }
386 |
387 |
388 | var _Platform_incomingPortMap = F2(function(tagger, finalTagger)
389 | {
390 | return function(value)
391 | {
392 | return tagger(finalTagger(value));
393 | };
394 | });
395 |
396 |
397 | function _Platform_setupIncomingPort(name, sendToApp)
398 | {
399 | var subs = __List_Nil;
400 | var converter = _Platform_effectManagers[name].__converter;
401 |
402 | // CREATE MANAGER
403 |
404 | var init = __Scheduler_succeed(null);
405 |
406 | _Platform_effectManagers[name].__init = init;
407 | _Platform_effectManagers[name].__onEffects = F3(function(router, subList, state)
408 | {
409 | subs = subList;
410 | return init;
411 | });
412 |
413 | // PUBLIC API
414 |
415 | function send(incomingValue)
416 | {
417 | var result = A2(__Json_run, converter, __Json_wrap(incomingValue));
418 |
419 | __Result_isOk(result) || __Error_throw(4, name, result.a);
420 |
421 | var value = result.a;
422 | for (var temp = subs; temp.b; temp = temp.b) // WHILE_CONS
423 | {
424 | sendToApp(temp.a(value));
425 | }
426 | }
427 |
428 | return { send: send };
429 | }
430 |
431 |
432 |
433 | // EXPORT ELM MODULES
434 | //
435 | // Have DEBUG and PROD versions so that we can (1) give nicer errors in
436 | // debug mode and (2) not pay for the bits needed for that in prod mode.
437 | //
438 |
439 |
440 | function _Platform_export__PROD(exports)
441 | {
442 | scope['Elm']
443 | ? _Platform_mergeExportsProd(scope['Elm'], exports)
444 | : scope['Elm'] = exports;
445 | }
446 |
447 |
448 | function _Platform_mergeExportsProd(obj, exports)
449 | {
450 | for (var name in exports)
451 | {
452 | (name in obj)
453 | ? (typeof name === 'function')
454 | ? __Error_throw(6)
455 | : _Platform_mergeExportsProd(obj[name], exports[name])
456 | : (obj[name] = exports[name]);
457 | }
458 | }
459 |
460 |
461 | function _Platform_export__DEBUG(exports)
462 | {
463 | scope['Elm']
464 | ? _Platform_mergeExportsDebug('Elm', scope['Elm'], exports)
465 | : scope['Elm'] = exports;
466 | }
467 |
468 |
469 | function _Platform_mergeExportsDebug(moduleName, obj, exports)
470 | {
471 | for (var name in exports)
472 | {
473 | (name in obj)
474 | ? (typeof name === 'function')
475 | ? __Error_throw(6, moduleName)
476 | : _Platform_mergeExportsDebug(moduleName + '.' + name, obj[name], exports[name])
477 | : (obj[name] = exports[name]);
478 | }
479 | }
480 |
--------------------------------------------------------------------------------
/tests/Test/Array.elm:
--------------------------------------------------------------------------------
1 | module Test.Array exposing (tests)
2 |
3 | import Array
4 | import Basics exposing (..)
5 | import List exposing ((::))
6 | import Maybe exposing (..)
7 | import Test exposing (..)
8 | import Fuzz exposing (Fuzzer, intRange)
9 | import Expect
10 |
11 |
12 | tests : Test
13 | tests =
14 | describe "Array"
15 | [ initTests
16 | , isEmptyTests
17 | , lengthTests
18 | , getSetTests
19 | , conversionTests
20 | , transformTests
21 | , sliceTests
22 | , runtimeCrashTests
23 | ]
24 |
25 |
26 | {-| > 33000 elements requires 3 levels in the tree
27 | -}
28 | defaultSizeRange : Fuzzer Int
29 | defaultSizeRange =
30 | (intRange 1 33000)
31 |
32 |
33 | initTests : Test
34 | initTests =
35 | describe "Initialization"
36 | [ fuzz defaultSizeRange "initialize" <|
37 | \size ->
38 | toList (initialize size identity)
39 | |> Expect.equal (List.range 0 (size - 1))
40 | , fuzz defaultSizeRange "push" <|
41 | \size ->
42 | List.foldl push empty (List.range 0 (size - 1))
43 | |> Expect.equal (initialize size identity)
44 | , test "initialize non-identity" <|
45 | \() ->
46 | toList (initialize 4 (\n -> n * n))
47 | |> Expect.equal [ 0, 1, 4, 9 ]
48 | , test "initialize empty" <|
49 | \() ->
50 | toList (initialize 0 identity)
51 | |> Expect.equal []
52 | , test "initialize negative" <|
53 | \() ->
54 | toList (initialize -2 identity)
55 | |> Expect.equal []
56 | ]
57 |
58 |
59 | isEmptyTests : Test
60 | isEmptyTests =
61 | describe "isEmpty"
62 | [ test "all empty arrays are equal" <|
63 | \() ->
64 | Expect.equal empty (fromList [])
65 | , test "empty array" <|
66 | \() ->
67 | isEmpty empty
68 | |> Expect.equal True
69 | , test "empty converted array" <|
70 | \() ->
71 | isEmpty (fromList [])
72 | |> Expect.equal True
73 | , test "non-empty array" <|
74 | \() ->
75 | isEmpty (fromList [ 1 ])
76 | |> Expect.equal False
77 | ]
78 |
79 |
80 | lengthTests : Test
81 | lengthTests =
82 | describe "Length"
83 | [ test "empty array" <|
84 | \() ->
85 | length empty
86 | |> Expect.equal 0
87 | , fuzz defaultSizeRange "non-empty array" <|
88 | \size ->
89 | length (initialize size identity)
90 | |> Expect.equal size
91 | , fuzz defaultSizeRange "push" <|
92 | \size ->
93 | length (push size (initialize size identity))
94 | |> Expect.equal (size + 1)
95 | , fuzz defaultSizeRange "append" <|
96 | \size ->
97 | length (append (initialize size identity) (initialize (size // 2) identity))
98 | |> Expect.equal (size + (size // 2))
99 | , fuzz defaultSizeRange "set does not increase" <|
100 | \size ->
101 | length (set (size // 2) 1 (initialize size identity))
102 | |> Expect.equal size
103 | , fuzz (intRange 100 10000) "big slice" <|
104 | \size ->
105 | length (slice 35 -35 (initialize size identity))
106 | |> Expect.equal (size - 70)
107 | , fuzz2 (intRange -32 -1) (intRange 100 10000) "small slice end" <|
108 | \n size ->
109 | length (slice 0 n (initialize size identity))
110 | |> Expect.equal (size + n)
111 | ]
112 |
113 |
114 | getSetTests : Test
115 | getSetTests =
116 | describe "Get and set"
117 | [ fuzz2 defaultSizeRange defaultSizeRange "can retrieve element" <|
118 | \x y ->
119 | let
120 | n =
121 | min x y
122 |
123 | size =
124 | max x y
125 | in
126 | get n (initialize (size + 1) identity)
127 | |> Expect.equal (Just n)
128 | , fuzz2 (intRange 1 50) (intRange 100 33000) "out of bounds retrieval returns nothing" <|
129 | \n size ->
130 | let
131 | arr =
132 | initialize size identity
133 | in
134 | ( get (negate n) arr
135 | , get (size + n) arr
136 | )
137 | |> Expect.equal ( Nothing, Nothing )
138 | , fuzz2 defaultSizeRange defaultSizeRange "set replaces value" <|
139 | \x y ->
140 | let
141 | n =
142 | min x y
143 |
144 | size =
145 | max x y
146 | in
147 | get n (set n 5 (initialize (size + 1) identity))
148 | |> Expect.equal (Just 5)
149 | , fuzz2 (intRange 1 50) defaultSizeRange "set out of bounds returns original array" <|
150 | \n size ->
151 | let
152 | arr =
153 | initialize size identity
154 | in
155 | set (negate n) 5 arr
156 | |> set (size + n) 5
157 | |> Expect.equal arr
158 | , test "Retrieval works from tail" <|
159 | \() ->
160 | get 1030 (set 1030 5 (initialize 1035 identity))
161 | |> Expect.equal (Just 5)
162 | ]
163 |
164 |
165 | conversionTests : Test
166 | conversionTests =
167 | describe "Conversion"
168 | [ fuzz defaultSizeRange "back and forth" <|
169 | \size ->
170 | let
171 | ls =
172 | List.range 0 (size - 1)
173 | in
174 | toList (fromList ls)
175 | |> Expect.equal ls
176 | , fuzz defaultSizeRange "indexed" <|
177 | \size ->
178 | toIndexedList (initialize size ((+) 1))
179 | |> Expect.equal (toList (initialize size (\idx -> ( idx, idx + 1 ))))
180 | ]
181 |
182 |
183 | transformTests : Test
184 | transformTests =
185 | describe "Transform"
186 | [ fuzz defaultSizeRange "foldl" <|
187 | \size ->
188 | foldl (::) [] (initialize size identity)
189 | |> Expect.equal (List.reverse (List.range 0 (size - 1)))
190 | , fuzz defaultSizeRange "foldr" <|
191 | \size ->
192 | foldr (\n acc -> n :: acc) [] (initialize size identity)
193 | |> Expect.equal (List.range 0 (size - 1))
194 | , fuzz defaultSizeRange "filter" <|
195 | \size ->
196 | toList (filter (\a -> a % 2 == 0) (initialize size identity))
197 | |> Expect.equal (List.filter (\a -> a % 2 == 0) (List.range 0 (size - 1)))
198 | , fuzz defaultSizeRange "map" <|
199 | \size ->
200 | map ((+) 1) (initialize size identity)
201 | |> Expect.equal (initialize size ((+) 1))
202 | , fuzz defaultSizeRange "indexedMap" <|
203 | \size ->
204 | indexedMap (*) (repeat size 5)
205 | |> Expect.equal (initialize size ((*) 5))
206 | , fuzz defaultSizeRange "push appends one element" <|
207 | \size ->
208 | push size (initialize size identity)
209 | |> Expect.equal (initialize (size + 1) identity)
210 | , fuzz (intRange 1 1050) "append" <|
211 | \size ->
212 | append (initialize size identity) (initialize size ((+) size))
213 | |> Expect.equal (initialize (size * 2) identity)
214 | , fuzz2 defaultSizeRange (intRange 1 32) "small appends" <|
215 | \s1 s2 ->
216 | append (initialize s1 identity) (initialize s2 ((+) s1))
217 | |> Expect.equal (initialize (s1 + s2) identity)
218 | , fuzz (defaultSizeRange) "toString" <|
219 | \size ->
220 | let
221 | ls =
222 | List.range 0 size
223 | in
224 | Array.toString (fromList ls)
225 | |> Expect.equal ("Array " ++ Basics.toString ls)
226 | ]
227 |
228 |
229 | sliceTests : Test
230 | sliceTests =
231 | let
232 | smallSample =
233 | fromList (List.range 1 8)
234 | in
235 | describe "Slice"
236 | [ fuzz2 (intRange -50 -1) (intRange 100 33000) "both" <|
237 | \n size ->
238 | slice (abs n) n (initialize size identity)
239 | |> Expect.equal (initialize (size + n + n) (\idx -> idx - n))
240 | , fuzz2 (intRange -50 -1) (intRange 100 33000) "left" <|
241 | \n size ->
242 | let
243 | arr =
244 | initialize size identity
245 | in
246 | slice (abs n) (length arr) arr
247 | |> Expect.equal (initialize (size + n) (\idx -> idx - n))
248 | , fuzz2 (intRange -50 -1) (intRange 100 33000) "right" <|
249 | \n size ->
250 | slice 0 n (initialize size identity)
251 | |> Expect.equal (initialize (size + n) identity)
252 | , fuzz defaultSizeRange "slicing all but the last item" <|
253 | \size ->
254 | initialize size identity
255 | |> slice -1 size
256 | |> toList
257 | |> Expect.equal [ size - 1 ]
258 | , test "both small" <|
259 | \() ->
260 | toList (slice 2 5 smallSample)
261 | |> Expect.equal (List.range 3 5)
262 | , test "start small" <|
263 | \() ->
264 | toList (slice 2 (length smallSample) smallSample)
265 | |> Expect.equal (List.range 3 8)
266 | , test "negative" <|
267 | \() ->
268 | toList (slice -5 -2 smallSample)
269 | |> Expect.equal (List.range 4 6)
270 | , test "impossible" <|
271 | \() ->
272 | toList (slice -1 -2 smallSample)
273 | |> Expect.equal []
274 | , test "crash" <|
275 | \() ->
276 | Array.repeat (33 * 32) 1
277 | |> Array.slice 0 1
278 | |> Expect.equal (Array.repeat 1 1)
279 | ]
280 |
281 |
282 | runtimeCrashTests : Test
283 | runtimeCrashTests =
284 | describe "Runtime crashes in core"
285 | [ test "magic slice" <|
286 | \() ->
287 | let
288 | n =
289 | 10
290 | in
291 | initialize (4 * n) identity
292 | |> slice n (4 * n)
293 | |> slice n (3 * n)
294 | |> slice n (2 * n)
295 | |> slice n n
296 | |> \a -> Expect.equal a a
297 | , test "magic slice 2" <|
298 | \() ->
299 | let
300 | ary =
301 | fromList <| List.range 0 32
302 |
303 | res =
304 | append (slice 1 32 ary) (slice (32 + 1) -1 ary)
305 | in
306 | Expect.equal res res
307 | , test "magic append" <|
308 | \() ->
309 | let
310 | res =
311 | append (initialize 1 (always 1))
312 | (initialize (32 ^ 2 - 1 * 32 + 1) (\i -> i))
313 | in
314 | Expect.equal res res
315 | ]
316 |
--------------------------------------------------------------------------------
/tests/Test/Basics.elm:
--------------------------------------------------------------------------------
1 | module Test.Basics exposing (tests)
2 |
3 | import Array
4 | import Tuple exposing (first, second)
5 | import Basics exposing (..)
6 | import Date
7 | import Set
8 | import Dict
9 | import Test exposing (..)
10 | import Expect
11 | import List
12 | import String
13 |
14 |
15 | tests : Test
16 | tests =
17 | let
18 | comparison =
19 | describe "Comparison"
20 | [ test "max" <| \() -> Expect.equal 42 (max 32 42)
21 | , test "min" <| \() -> Expect.equal 42 (min 91 42)
22 | , test "clamp low" <| \() -> Expect.equal 10 (clamp 10 20 5)
23 | , test "clamp mid" <| \() -> Expect.equal 15 (clamp 10 20 15)
24 | , test "clamp high" <| \() -> Expect.equal 20 (clamp 10 20 25)
25 | , test "5 < 6" <| \() -> Expect.equal True (5 < 6)
26 | , test "6 < 5" <| \() -> Expect.equal False (6 < 5)
27 | , test "6 < 6" <| \() -> Expect.equal False (6 < 6)
28 | , test "5 > 6" <| \() -> Expect.equal False (5 > 6)
29 | , test "6 > 5" <| \() -> Expect.equal True (6 > 5)
30 | , test "6 > 6" <| \() -> Expect.equal False (6 > 6)
31 | , test "5 <= 6" <| \() -> Expect.equal True (5 <= 6)
32 | , test "6 <= 5" <| \() -> Expect.equal False (6 <= 5)
33 | , test "6 <= 6" <| \() -> Expect.equal True (6 <= 6)
34 | , test "compare \"A\" \"B\"" <| \() -> Expect.equal LT (compare "A" "B")
35 | , test "compare 'f' 'f'" <| \() -> Expect.equal EQ (compare 'f' 'f')
36 | , test "compare (1, 2, 3, 4, 5, 6) (0, 1, 2, 3, 4, 5)" <| \() -> Expect.equal GT (compare ( 1, 2, 3, 4, 5, 6 ) ( 0, 1, 2, 3, 4, 5 ))
37 | , test "compare ['a'] ['b']" <| \() -> Expect.equal LT (compare [ 'a' ] [ 'b' ])
38 | , test "array equality" <| \() -> Expect.equal (Array.fromList [ 1, 1, 1, 1 ]) (Array.repeat 4 1)
39 | , test "set equality" <| \() -> Expect.equal (Set.fromList [ 1, 2 ]) (Set.fromList [ 2, 1 ])
40 | , test "dict equality" <| \() -> Expect.equal (Dict.fromList [ ( 1, 1 ), ( 2, 2 ) ]) (Dict.fromList [ ( 2, 2 ), ( 1, 1 ) ])
41 | , test "char equality" <| \() -> Expect.notEqual '0' '饑'
42 | , test "date equality" <| \() -> Expect.equal (Date.fromString "2/7/1992") (Date.fromString "2/7/1992")
43 | , test "date equality" <| \() -> Expect.notEqual (Date.fromString "11/16/1995") (Date.fromString "2/7/1992")
44 | ]
45 |
46 | toStringTests =
47 | describe "toString Tests"
48 | [ test "toString Int" <| \() -> Expect.equal "42" (toString 42)
49 | , test "toString Float" <| \() -> Expect.equal "42.52" (toString 42.52)
50 | , test "toString Char" <| \() -> Expect.equal "'c'" (toString 'c')
51 | , test "toString Char single quote" <| \() -> Expect.equal "'\\''" (toString '\'')
52 | , test "toString Char double quote" <| \() -> Expect.equal "'\"'" (toString '"')
53 | , test "toString String single quote" <| \() -> Expect.equal "\"not 'escaped'\"" (toString "not 'escaped'")
54 | , test "toString String double quote" <| \() -> Expect.equal "\"are \\\"escaped\\\"\"" (toString "are \"escaped\"")
55 | , test "toString record" <| \() -> Expect.equal "{ field = [0] }" (toString { field = [ 0 ] })
56 | -- TODO
57 | --, test "toString record, special case" <| \() -> Expect.equal "{ ctor = [0] }" (toString { ctor = [ 0 ] })
58 | ]
59 |
60 | trigTests =
61 | describe "Trigonometry Tests"
62 | [ test "radians 0" <| \() -> Expect.equal 0 (radians 0)
63 | , test "radians positive" <| \() -> Expect.equal 5 (radians 5)
64 | , test "radians negative" <| \() -> Expect.equal -5 (radians -5)
65 | , test "degrees 0" <| \() -> Expect.equal 0 (degrees 0)
66 | , test "degrees 90" <| \() -> Expect.lessThan 0.01 (abs (1.57 - degrees 90))
67 | -- This should test to enough precision to know if anything's breaking
68 | , test "degrees -145" <| \() -> Expect.lessThan 0.01 (abs (-2.53 - degrees -145))
69 | -- This should test to enough precision to know if anything's breaking
70 | , test "turns 0" <| \() -> Expect.equal 0 (turns 0)
71 | , test "turns 8" <| \() -> Expect.lessThan 0.01 (abs (50.26 - turns 8))
72 | -- This should test to enough precision to know if anything's breaking
73 | , test "turns -133" <| \() -> Expect.lessThan 0.01 (abs (-835.66 - turns -133))
74 | -- This should test to enough precision to know if anything's breaking
75 | , test "fromPolar (0, 0)" <| \() -> Expect.equal ( 0, 0 ) (fromPolar ( 0, 0 ))
76 | , test "fromPolar (1, 0)" <| \() -> Expect.equal ( 1, 0 ) (fromPolar ( 1, 0 ))
77 | , test "fromPolar (0, 1)" <| \() -> Expect.equal ( 0, 0 ) (fromPolar ( 0, 1 ))
78 | , test "fromPolar (1, 1)" <|
79 | \() ->
80 | Expect.equal True
81 | (let
82 | ( x, y ) =
83 | fromPolar ( 1, 1 )
84 | in
85 | 0.54 - x < 0.01 && 0.84 - y < 0.01
86 | )
87 | , test "toPolar (0, 0)" <| \() -> Expect.equal ( 0, 0 ) (toPolar ( 0, 0 ))
88 | , test "toPolar (1, 0)" <| \() -> Expect.equal ( 1, 0 ) (toPolar ( 1, 0 ))
89 | , test "toPolar (0, 1)" <|
90 | \() ->
91 | Expect.equal True
92 | (let
93 | ( r, theta ) =
94 | toPolar ( 0, 1 )
95 | in
96 | r == 1 && abs (1.57 - theta) < 0.01
97 | )
98 | , test "toPolar (1, 1)" <|
99 | \() ->
100 | Expect.equal True
101 | (let
102 | ( r, theta ) =
103 | toPolar ( 1, 1 )
104 | in
105 | abs (1.41 - r) < 0.01 && abs (0.78 - theta) < 0.01
106 | )
107 | , test "cos" <| \() -> Expect.equal 1 (cos 0)
108 | , test "sin" <| \() -> Expect.equal 0 (sin 0)
109 | , test "tan" <| \() -> Expect.lessThan 0.01 (abs (12.67 - tan 17.2))
110 | , test "acos" <| \() -> Expect.lessThan 0.01 (abs (3.14 - acos -1))
111 | , test "asin" <| \() -> Expect.lessThan 0.01 (abs (0.3 - asin 0.3))
112 | , test "atan" <| \() -> Expect.lessThan 0.01 (abs (1.57 - atan 4567.8))
113 | , test "atan2" <| \() -> Expect.lessThan 0.01 (abs (1.55 - atan2 36 0.65))
114 | , test "pi" <| \() -> Expect.lessThan 0.01 (abs (3.14 - pi))
115 | ]
116 |
117 | basicMathTests =
118 | describe "Basic Math Tests"
119 | [ test "add float" <| \() -> Expect.equal 159 (155.6 + 3.4)
120 | , test "add int" <| \() -> Expect.equal 17 ((round 10) + (round 7))
121 | , test "subtract float" <| \() -> Expect.equal -6.3 (1 - 7.3)
122 | , test "subtract int" <| \() -> Expect.equal 1130 ((round 9432) - (round 8302))
123 | , test "multiply float" <| \() -> Expect.equal 432 (96 * 4.5)
124 | , test "multiply int" <| \() -> Expect.equal 90 ((round 10) * (round 9))
125 | , test "divide float" <| \() -> Expect.equal 13.175 (527 / 40)
126 | , test "divide int" <| \() -> Expect.equal 23 (70 // 3)
127 | , test "2 |> rem 7" <| \() -> Expect.equal 1 (2 |> rem 7)
128 | , test "4 |> rem -1" <| \() -> Expect.equal -1 (4 |> rem -1)
129 | , test "7 % 2" <| \() -> Expect.equal 1 (7 % 2)
130 | , test "-1 % 4" <| \() -> Expect.equal 3 (-1 % 4)
131 | , test "3^2" <| \() -> Expect.equal 9 (3 ^ 2)
132 | , test "sqrt" <| \() -> Expect.equal 9 (sqrt 81)
133 | , test "negate 42" <| \() -> Expect.equal -42 (negate 42)
134 | , test "negate -42" <| \() -> Expect.equal 42 (negate -42)
135 | , test "negate 0" <| \() -> Expect.equal 0 (negate 0)
136 | , test "abs -25" <| \() -> Expect.equal 25 (abs -25)
137 | , test "abs 76" <| \() -> Expect.equal 76 (abs 76)
138 | , test "logBase 10 100" <| \() -> Expect.equal 2 (logBase 10 100)
139 | , test "logBase 2 256" <| \() -> Expect.equal 8 (logBase 2 256)
140 | , test "e" <| \() -> Expect.lessThan 0.01 (abs (2.72 - e))
141 | ]
142 |
143 | booleanTests =
144 | describe "Boolean Tests"
145 | [ test "False && False" <| \() -> Expect.equal False (False && False)
146 | , test "False && True" <| \() -> Expect.equal False (False && True)
147 | , test "True && False" <| \() -> Expect.equal False (True && False)
148 | , test "True && True" <| \() -> Expect.equal True (True && True)
149 | , test "False || False" <| \() -> Expect.equal False (False || False)
150 | , test "False || True" <| \() -> Expect.equal True (False || True)
151 | , test "True || False" <| \() -> Expect.equal True (True || False)
152 | , test "True || True" <| \() -> Expect.equal True (True || True)
153 | , test "xor False False" <| \() -> Expect.equal False (xor False False)
154 | , test "xor False True" <| \() -> Expect.equal True (xor False True)
155 | , test "xor True False" <| \() -> Expect.equal True (xor True False)
156 | , test "xor True True" <| \() -> Expect.equal False (xor True True)
157 | , test "not True" <| \() -> Expect.equal False (not True)
158 | , test "not False" <| \() -> Expect.equal True (not False)
159 | ]
160 |
161 | conversionTests =
162 | describe "Conversion Tests"
163 | [ test "round 0.6" <| \() -> Expect.equal 1 (round 0.6)
164 | , test "round 0.4" <| \() -> Expect.equal 0 (round 0.4)
165 | , test "round 0.5" <| \() -> Expect.equal 1 (round 0.5)
166 | , test "truncate -2367.9267" <| \() -> Expect.equal -2367 (truncate -2367.9267)
167 | , test "floor -2367.9267" <| \() -> Expect.equal -2368 (floor -2367.9267)
168 | , test "ceiling 37.2" <| \() -> Expect.equal 38 (ceiling 37.2)
169 | , test "toFloat 25" <| \() -> Expect.equal 25 (toFloat 25)
170 | ]
171 |
172 | miscTests =
173 | describe "Miscellaneous Tests"
174 | [ test "isNaN (0/0)" <| \() -> Expect.equal True (isNaN (0 / 0))
175 | , test "isNaN (sqrt -1)" <| \() -> Expect.equal True (isNaN (sqrt -1))
176 | , test "isNaN (1/0)" <| \() -> Expect.equal False (isNaN (1 / 0))
177 | , test "isNaN 1" <| \() -> Expect.equal False (isNaN 1)
178 | , test "isInfinite (0/0)" <| \() -> Expect.equal False (isInfinite (0 / 0))
179 | , test "isInfinite (sqrt -1)" <| \() -> Expect.equal False (isInfinite (sqrt -1))
180 | , test "isInfinite (1/0)" <| \() -> Expect.equal True (isInfinite (1 / 0))
181 | , test "isInfinite 1" <| \() -> Expect.equal False (isInfinite 1)
182 | , test "\"hello\" ++ \"world\"" <| \() -> Expect.equal "helloworld" ("hello" ++ "world")
183 | , test "[1, 1, 2] ++ [3, 5, 8]" <| \() -> Expect.equal [ 1, 1, 2, 3, 5, 8 ] ([ 1, 1, 2 ] ++ [ 3, 5, 8 ])
184 | , test "first (1, 2)" <| \() -> Expect.equal 1 (first ( 1, 2 ))
185 | , test "second (1, 2)" <| \() -> Expect.equal 2 (second ( 1, 2 ))
186 | ]
187 |
188 | higherOrderTests =
189 | describe "Higher Order Helpers"
190 | [ test "identity 'c'" <| \() -> Expect.equal 'c' (identity 'c')
191 | , test "always 42 ()" <| \() -> Expect.equal 42 (always 42 ())
192 | , test "<|" <| \() -> Expect.equal 9 (identity <| 3 + 6)
193 | , test "|>" <| \() -> Expect.equal 9 (3 + 6 |> identity)
194 | , test "<<" <| \() -> Expect.equal True (not << xor True <| True)
195 | , test "<<" <| \() -> Expect.equal True (not << xor True <| True)
196 | , describe ">>"
197 | [ test "with xor" <|
198 | \() ->
199 | (True |> xor True >> not)
200 | |> Expect.equal True
201 | , test "with a record accessor" <|
202 | \() ->
203 | [ { foo = "NaS", bar = "baz" } ]
204 | |> List.map (.foo >> String.reverse)
205 | |> Expect.equal [ "SaN" ]
206 | ]
207 | , test "flip" <| \() -> Expect.equal 10 ((flip (//)) 2 20)
208 | , test "curry" <| \() -> Expect.equal 1 ((curry (\( a, b ) -> a + b)) -5 6)
209 | , test "uncurry" <| \() -> Expect.equal 1 ((uncurry (+)) ( -5, 6 ))
210 | ]
211 | in
212 | describe "Basics"
213 | [ comparison
214 | , toStringTests
215 | , trigTests
216 | , basicMathTests
217 | , booleanTests
218 | , miscTests
219 | , higherOrderTests
220 | ]
221 |
--------------------------------------------------------------------------------
/src/String.elm:
--------------------------------------------------------------------------------
1 | module String exposing
2 | ( String
3 | , isEmpty, length, reverse, repeat, replace
4 | , append, concat, split, join, words, lines
5 | , slice, left, right, dropLeft, dropRight
6 | , contains, startsWith, endsWith, indexes, indices
7 | , toInt, fromInt
8 | , toFloat, fromFloat
9 | , fromChar, cons, uncons
10 | , toList, fromList
11 | , toUpper, toLower, pad, padLeft, padRight, trim, trimLeft, trimRight
12 | , map, filter, foldl, foldr, any, all
13 | )
14 |
15 | {-| A built-in representation for efficient string manipulation. String literals
16 | are enclosed in `"double quotes"`. Strings are *not* lists of characters.
17 |
18 | # Strings
19 | @docs String, isEmpty, length, reverse, repeat, replace
20 |
21 | # Building and Splitting
22 | @docs append, concat, split, join, words, lines
23 |
24 | # Get Substrings
25 | @docs slice, left, right, dropLeft, dropRight
26 |
27 | # Check for Substrings
28 | @docs contains, startsWith, endsWith, indexes, indices
29 |
30 | # Int Conversions
31 | @docs toInt, fromInt
32 |
33 | # Float Conversions
34 | @docs toFloat, fromFloat
35 |
36 | # Char Conversions
37 | @docs fromChar, cons, uncons
38 |
39 | # List Conversions
40 | @docs toList, fromList
41 |
42 | # Formatting
43 | Cosmetic operations such as padding with extra characters or trimming whitespace.
44 |
45 | @docs toUpper, toLower, pad, padLeft, padRight, trim, trimLeft, trimRight
46 |
47 | # Higher-Order Functions
48 | @docs map, filter, foldl, foldr, any, all
49 | -}
50 |
51 | import Basics exposing (..)
52 | import Bitwise
53 | import Char exposing (Char)
54 | import Elm.Kernel.List
55 | import Elm.Kernel.String
56 | import List exposing ((::))
57 | import Maybe exposing (Maybe)
58 | import Result exposing (Result)
59 |
60 |
61 |
62 | -- STRINGS
63 |
64 |
65 | {-| A `String` is a chunk of text:
66 |
67 | "Hello!"
68 | "How are you?"
69 | "🙈🙉🙊"
70 |
71 | -- strings with escape characters
72 | "this\n\t\"that\""
73 | "\u{1F648}\u{1F649}\u{1F64A}" -- "🙈🙉🙊"
74 |
75 | -- multiline strings
76 | """Triple double quotes let you
77 | create "multiline strings" which
78 | can have unescaped quotes and newlines.
79 | """
80 |
81 | A `String` can represent any sequence of [unicode characters][u]. You can use
82 | the unicode escapes from `\u{0000}` to `\u{10FFFF}` to represent characters
83 | by their code point. You can also include the unicode characters directly.
84 | Using the escapes can be better if you need one of the many whitespace
85 | characters with different widths.
86 |
87 | [u]: https://en.wikipedia.org/wiki/Unicode
88 |
89 | **Note:** JavaScript lets you use double quotes and single quotes interchangably.
90 | This is not true in Elm. You must use double quotes for a `String`, and you must
91 | use single quotes for a [`Char`](Char#Char).
92 | -}
93 | type String = String -- NOTE: The compiler provides the real implementation.
94 |
95 |
96 | {-| Determine if a string is empty.
97 |
98 | isEmpty "" == True
99 | isEmpty "the world" == False
100 | -}
101 | isEmpty : String -> Bool
102 | isEmpty string =
103 | string == ""
104 |
105 |
106 | {-| Get the length of a string.
107 |
108 | length "innumerable" == 11
109 | length "" == 0
110 |
111 | -}
112 | length : String -> Int
113 | length =
114 | Elm.Kernel.String.length
115 |
116 |
117 | {-| Reverse a string.
118 |
119 | reverse "stressed" == "desserts"
120 | -}
121 | reverse : String -> String
122 | reverse =
123 | Elm.Kernel.String.reverse
124 |
125 |
126 | {-| Repeat a string *n* times.
127 |
128 | repeat 3 "ha" == "hahaha"
129 | -}
130 | repeat : Int -> String -> String
131 | repeat n chunk =
132 | repeatHelp n chunk ""
133 |
134 |
135 | repeatHelp : Int -> String -> String -> String
136 | repeatHelp n chunk result =
137 | if n <= 0 then
138 | result
139 | else
140 | repeatHelp (Bitwise.shiftRightBy 1 n) (chunk ++ chunk) <|
141 | if Bitwise.and n 1 == 0 then result else result ++ chunk
142 |
143 |
144 | {-| Replace all occurrences of some substring.
145 |
146 | replace "." "-" "Json.Decode.succeed" == "Json-Decode-succeed"
147 | replace "," "/" "a,b,c,d,e" == "a/b/c/d/e"
148 |
149 | **Note:** If you need more advanced replacements, check out the
150 | `elm-lang/parser` or `elm-lang/regexp` package.
151 | -}
152 | replace : String -> String -> String -> String
153 | replace before after string =
154 | join after (split before string)
155 |
156 |
157 |
158 | -- BUILDING AND SPLITTING
159 |
160 |
161 | {-| Append two strings. You can also use [the `(++)` operator](Basics#++)
162 | to do this.
163 |
164 | append "butter" "fly" == "butterfly"
165 | -}
166 | append : String -> String -> String
167 | append =
168 | Elm.Kernel.String.append
169 |
170 |
171 | {-| Concatenate many strings into one.
172 |
173 | concat ["never","the","less"] == "nevertheless"
174 | -}
175 | concat : List String -> String
176 | concat strings =
177 | join "" strings
178 |
179 |
180 | {-| Split a string using a given separator.
181 |
182 | split "," "cat,dog,cow" == ["cat","dog","cow"]
183 | split "/" "home/evan/Desktop/" == ["home","evan","Desktop", ""]
184 |
185 | -}
186 | split : String -> String -> List String
187 | split sep string =
188 | Elm.Kernel.List.fromArray (Elm.Kernel.String.split sep string)
189 |
190 |
191 | {-| Put many strings together with a given separator.
192 |
193 | join "a" ["H","w","ii","n"] == "Hawaiian"
194 | join " " ["cat","dog","cow"] == "cat dog cow"
195 | join "/" ["home","evan","Desktop"] == "home/evan/Desktop"
196 | -}
197 | join : String -> List String -> String
198 | join sep chunks =
199 | Elm.Kernel.String.join sep (Elm.Kernel.List.toArray chunks)
200 |
201 |
202 | {-| Break a string into words, splitting on chunks of whitespace.
203 |
204 | words "How are \t you? \n Good?" == ["How","are","you?","Good?"]
205 | -}
206 | words : String -> List String
207 | words =
208 | Elm.Kernel.String.words
209 |
210 |
211 | {-| Break a string into lines, splitting on newlines.
212 |
213 | lines "How are you?\nGood?" == ["How are you?", "Good?"]
214 | -}
215 | lines : String -> List String
216 | lines =
217 | Elm.Kernel.String.lines
218 |
219 |
220 |
221 | -- SUBSTRINGS
222 |
223 |
224 | {-| Take a substring given a start and end index. Negative indexes
225 | are taken starting from the *end* of the list.
226 |
227 | slice 7 9 "snakes on a plane!" == "on"
228 | slice 0 6 "snakes on a plane!" == "snakes"
229 | slice 0 -7 "snakes on a plane!" == "snakes on a"
230 | slice -6 -1 "snakes on a plane!" == "plane"
231 | -}
232 | slice : Int -> Int -> String -> String
233 | slice =
234 | Elm.Kernel.String.slice
235 |
236 |
237 | {-| Take *n* characters from the left side of a string.
238 |
239 | left 2 "Mulder" == "Mu"
240 | -}
241 | left : Int -> String -> String
242 | left n string =
243 | if n < 1 then
244 | ""
245 | else
246 | slice 0 n string
247 |
248 |
249 | {-| Take *n* characters from the right side of a string.
250 |
251 | right 2 "Scully" == "ly"
252 | -}
253 | right : Int -> String -> String
254 | right n string =
255 | if n < 1 then
256 | ""
257 | else
258 | slice -n (length string) string
259 |
260 |
261 | {-| Drop *n* characters from the left side of a string.
262 |
263 | dropLeft 2 "The Lone Gunmen" == "e Lone Gunmen"
264 | -}
265 | dropLeft : Int -> String -> String
266 | dropLeft n string =
267 | if n < 1 then
268 | string
269 | else
270 | slice n (length string) string
271 |
272 |
273 | {-| Drop *n* characters from the right side of a string.
274 |
275 | dropRight 2 "Cigarette Smoking Man" == "Cigarette Smoking M"
276 | -}
277 | dropRight : Int -> String -> String
278 | dropRight n string =
279 | if n < 1 then
280 | string
281 | else
282 | slice 0 -n string
283 |
284 |
285 |
286 | -- DETECT SUBSTRINGS
287 |
288 |
289 | {-| See if the second string contains the first one.
290 |
291 | contains "the" "theory" == True
292 | contains "hat" "theory" == False
293 | contains "THE" "theory" == False
294 |
295 | -}
296 | contains : String -> String -> Bool
297 | contains =
298 | Elm.Kernel.String.contains
299 |
300 |
301 | {-| See if the second string starts with the first one.
302 |
303 | startsWith "the" "theory" == True
304 | startsWith "ory" "theory" == False
305 | -}
306 | startsWith : String -> String -> Bool
307 | startsWith =
308 | Elm.Kernel.String.startsWith
309 |
310 |
311 | {-| See if the second string ends with the first one.
312 |
313 | endsWith "the" "theory" == False
314 | endsWith "ory" "theory" == True
315 | -}
316 | endsWith : String -> String -> Bool
317 | endsWith =
318 | Elm.Kernel.String.endsWith
319 |
320 |
321 | {-| Get all of the indexes for a substring in another string.
322 |
323 | indexes "i" "Mississippi" == [1,4,7,10]
324 | indexes "ss" "Mississippi" == [2,5]
325 | indexes "needle" "haystack" == []
326 | -}
327 | indexes : String -> String -> List Int
328 | indexes =
329 | Elm.Kernel.String.indexes
330 |
331 |
332 | {-| Alias for `indexes`. -}
333 | indices : String -> String -> List Int
334 | indices =
335 | Elm.Kernel.String.indexes
336 |
337 |
338 |
339 | -- FORMATTING
340 |
341 |
342 | {-| Convert a string to all upper case. Useful for case-insensitive comparisons
343 | and VIRTUAL YELLING.
344 |
345 | toUpper "skinner" == "SKINNER"
346 | -}
347 | toUpper : String -> String
348 | toUpper =
349 | Elm.Kernel.String.toUpper
350 |
351 |
352 | {-| Convert a string to all lower case. Useful for case-insensitive comparisons.
353 |
354 | toLower "X-FILES" == "x-files"
355 | -}
356 | toLower : String -> String
357 | toLower =
358 | Elm.Kernel.String.toLower
359 |
360 |
361 | {-| Pad a string on both sides until it has a given length.
362 |
363 | pad 5 ' ' "1" == " 1 "
364 | pad 5 ' ' "11" == " 11 "
365 | pad 5 ' ' "121" == " 121 "
366 | -}
367 | pad : Int -> Char -> String -> String
368 | pad n char string =
369 | let
370 | half =
371 | Basics.toFloat (n - length string) / 2
372 | in
373 | repeat (ceiling half) (fromChar char) ++ string ++ repeat (floor half) (fromChar char)
374 |
375 |
376 | {-| Pad a string on the left until it has a given length.
377 |
378 | padLeft 5 '.' "1" == "....1"
379 | padLeft 5 '.' "11" == "...11"
380 | padLeft 5 '.' "121" == "..121"
381 | -}
382 | padLeft : Int -> Char -> String -> String
383 | padLeft n char string =
384 | repeat (n - length string) (fromChar char) ++ string
385 |
386 |
387 | {-| Pad a string on the right until it has a given length.
388 |
389 | padRight 5 '.' "1" == "1...."
390 | padRight 5 '.' "11" == "11..."
391 | padRight 5 '.' "121" == "121.."
392 | -}
393 | padRight : Int -> Char -> String -> String
394 | padRight n char string =
395 | string ++ repeat (n - length string) (fromChar char)
396 |
397 |
398 | {-| Get rid of whitespace on both sides of a string.
399 |
400 | trim " hats \n" == "hats"
401 | -}
402 | trim : String -> String
403 | trim =
404 | Elm.Kernel.String.trim
405 |
406 |
407 | {-| Get rid of whitespace on the left of a string.
408 |
409 | trimLeft " hats \n" == "hats \n"
410 | -}
411 | trimLeft : String -> String
412 | trimLeft =
413 | Elm.Kernel.String.trimLeft
414 |
415 |
416 | {-| Get rid of whitespace on the right of a string.
417 |
418 | trimRight " hats \n" == " hats"
419 | -}
420 | trimRight : String -> String
421 | trimRight =
422 | Elm.Kernel.String.trimRight
423 |
424 |
425 |
426 | -- INT CONVERSIONS
427 |
428 |
429 | {-| Try to convert a string into an int, failing on improperly formatted strings.
430 |
431 | String.toInt "123" == Just 123
432 | String.toInt "-42" == Just -42
433 | String.toInt "3.1" == Nothing
434 | String.toInt "31a" == Nothing
435 |
436 | If you are extracting a number from some raw user input, you will typically
437 | want to use [`Maybe.withDefault`](Maybe#withDefault) to handle bad data:
438 |
439 | Maybe.withDefault 0 (String.toInt "42") == 42
440 | Maybe.withDefault 0 (String.toInt "ab") == 0
441 | -}
442 | toInt : String -> Maybe Int
443 | toInt =
444 | Elm.Kernel.String.toInt
445 |
446 |
447 | {-| Convert an `Int` to a `String`.
448 |
449 | String.fromInt 123 == "123"
450 | String.fromInt -42 == "-42"
451 |
452 | Check out [`Debug.toString`](Debug#toString) to convert *any* value to a string
453 | for debugging purposes.
454 | -}
455 | fromInt : Int -> String
456 | fromInt =
457 | Elm.Kernel.String.fromNumber
458 |
459 |
460 |
461 | -- FLOAT CONVERSIONS
462 |
463 |
464 | {-| Try to convert a string into a float, failing on improperly formatted strings.
465 |
466 | String.toFloat "123" == Just 123.0
467 | String.toFloat "-42" == Just -42.0
468 | String.toFloat "3.1" == Just 3.1
469 | String.toFloat "31a" == Nothing
470 |
471 | If you are extracting a number from some raw user input, you will typically
472 | want to use [`Maybe.withDefault`](Maybe#withDefault) to handle bad data:
473 |
474 | Maybe.withDefault 0 (String.toFloat "42.5") == 42.5
475 | Maybe.withDefault 0 (String.toFloat "cats") == 0
476 | -}
477 | toFloat : String -> Maybe Float
478 | toFloat =
479 | Elm.Kernel.String.toFloat
480 |
481 |
482 | {-| Convert a `Float` to a `String`.
483 |
484 | String.fromFloat 123 == "123"
485 | String.fromFloat -42 == "-42"
486 | String.fromFloat 3.9 == "3.9"
487 |
488 | Check out [`Debug.toString`](Debug#toString) to convert *any* value to a string
489 | for debugging purposes.
490 | -}
491 | fromFloat : Float -> String
492 | fromFloat =
493 | Elm.Kernel.String.fromNumber
494 |
495 |
496 |
497 | -- LIST CONVERSIONS
498 |
499 |
500 | {-| Convert a string to a list of characters.
501 |
502 | toList "abc" == ['a','b','c']
503 | toList "🙈🙉🙊" == ['🙈','🙉','🙊']
504 | -}
505 | toList : String -> List Char
506 | toList string =
507 | foldr (::) [] string
508 |
509 |
510 | {-| Convert a list of characters into a String. Can be useful if you
511 | want to create a string primarily by consing, perhaps for decoding
512 | something.
513 |
514 | fromList ['a','b','c'] == "abc"
515 | fromList ['🙈','🙉','🙊'] == "🙈🙉🙊"
516 | -}
517 | fromList : List Char -> String
518 | fromList =
519 | Elm.Kernel.String.fromList
520 |
521 |
522 |
523 | -- CHAR CONVERSIONS
524 |
525 |
526 | {-| Create a string from a given character.
527 |
528 | fromChar 'a' == "a"
529 | -}
530 | fromChar : Char -> String
531 | fromChar char =
532 | cons char ""
533 |
534 |
535 | {-| Add a character to the beginning of a string.
536 |
537 | cons 'T' "he truth is out there" == "The truth is out there"
538 | -}
539 | cons : Char -> String -> String
540 | cons =
541 | Elm.Kernel.String.cons
542 |
543 |
544 | {-| Split a non-empty string into its head and tail. This lets you
545 | pattern match on strings exactly as you would with lists.
546 |
547 | uncons "abc" == Just ('a',"bc")
548 | uncons "" == Nothing
549 | -}
550 | uncons : String -> Maybe (Char, String)
551 | uncons =
552 | Elm.Kernel.String.uncons
553 |
554 |
555 |
556 | -- HIGHER-ORDER FUNCTIONS
557 |
558 |
559 | {-| Transform every character in a string
560 |
561 | map (\c -> if c == '/' then '.' else c) "a/b/c" == "a.b.c"
562 | -}
563 | map : (Char -> Char) -> String -> String
564 | map =
565 | Elm.Kernel.String.map
566 |
567 |
568 | {-| Keep only the characters that pass the test.
569 |
570 | filter isDigit "R2-D2" == "22"
571 | -}
572 | filter : (Char -> Bool) -> String -> String
573 | filter =
574 | Elm.Kernel.String.filter
575 |
576 |
577 | {-| Reduce a string from the left.
578 |
579 | foldl cons "" "time" == "emit"
580 | -}
581 | foldl : (Char -> b -> b) -> b -> String -> b
582 | foldl =
583 | Elm.Kernel.String.foldl
584 |
585 |
586 | {-| Reduce a string from the right.
587 |
588 | foldr cons "" "time" == "time"
589 | -}
590 | foldr : (Char -> b -> b) -> b -> String -> b
591 | foldr =
592 | Elm.Kernel.String.foldr
593 |
594 |
595 | {-| Determine whether *any* characters pass the test.
596 |
597 | any isDigit "90210" == True
598 | any isDigit "R2-D2" == True
599 | any isDigit "heart" == False
600 | -}
601 | any : (Char -> Bool) -> String -> Bool
602 | any =
603 | Elm.Kernel.String.any
604 |
605 |
606 | {-| Determine whether *all* characters pass the test.
607 |
608 | all isDigit "90210" == True
609 | all isDigit "R2-D2" == False
610 | all isDigit "heart" == False
611 | -}
612 | all : (Char -> Bool) -> String -> Bool
613 | all =
614 | Elm.Kernel.String.all
615 |
--------------------------------------------------------------------------------