├── .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 | --------------------------------------------------------------------------------