rm -rf finished/ for now
This commit is contained in:
@@ -1,12 +0,0 @@
|
|||||||
# Finished
|
|
||||||
|
|
||||||
This is how the app looks when everything is finished!
|
|
||||||
|
|
||||||
To build everything, `cd` into the `finished/` directory and run:
|
|
||||||
|
|
||||||
```shell
|
|
||||||
elm-live src/Main.elm --output=../server/public/elm.js --dir=../server/public --pushstate
|
|
||||||
```
|
|
||||||
|
|
||||||
Then open [http://localhost:3000](http://localhost:3000) in your browser.
|
|
||||||
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "1.0.0",
|
|
||||||
"summary": "helpful summary of your project, less than 80 characters",
|
|
||||||
"repository": "https://github.com/user/project.git",
|
|
||||||
"license": "BSD3",
|
|
||||||
"source-directories": [
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"exposed-modules": [],
|
|
||||||
"dependencies": {
|
|
||||||
"NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0",
|
|
||||||
"elm-community/json-extra": "2.1.0 <= v < 3.0.0",
|
|
||||||
"elm-lang/core": "5.1.1 <= v < 6.0.0",
|
|
||||||
"elm-lang/dom": "1.1.1 <= v < 2.0.0",
|
|
||||||
"elm-lang/html": "2.0.0 <= v < 3.0.0",
|
|
||||||
"elm-lang/http": "1.0.0 <= v < 2.0.0",
|
|
||||||
"elm-lang/navigation": "2.1.0 <= v < 3.0.0",
|
|
||||||
"evancz/elm-markdown": "3.0.2 <= v < 4.0.0",
|
|
||||||
"evancz/url-parser": "2.0.1 <= v < 3.0.0",
|
|
||||||
"lukewestby/elm-http-builder": "5.1.0 <= v < 6.0.0",
|
|
||||||
"mgold/elm-date-format": "1.3.0 <= v < 2.0.0",
|
|
||||||
"rtfeldman/elm-validate": "3.0.0 <= v < 4.0.0",
|
|
||||||
"rtfeldman/selectlist": "1.0.0 <= v < 2.0.0"
|
|
||||||
},
|
|
||||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"rtfeldman/elm-validate": "3.0.0",
|
|
||||||
"rtfeldman/selectlist": "1.0.0",
|
|
||||||
"elm-lang/navigation": "2.1.0",
|
|
||||||
"elm-lang/virtual-dom": "2.0.4",
|
|
||||||
"evancz/url-parser": "2.0.1",
|
|
||||||
"mgold/elm-date-format": "1.5.0",
|
|
||||||
"evancz/elm-markdown": "3.0.2",
|
|
||||||
"elm-lang/dom": "1.1.1",
|
|
||||||
"elm-lang/html": "2.0.0",
|
|
||||||
"elm-community/json-extra": "2.7.0",
|
|
||||||
"elm-lang/http": "1.0.0",
|
|
||||||
"lukewestby/elm-http-builder": "5.2.0",
|
|
||||||
"NoRedInk/elm-decode-pipeline": "3.0.0",
|
|
||||||
"elm-lang/core": "5.1.1"
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
# elm-package generated files
|
|
||||||
elm-stuff/
|
|
||||||
# elm-repl generated files
|
|
||||||
repl-temp-*
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
Copyright (c) 2016, NoRedInk
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright notice, this
|
|
||||||
list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above copyright notice,
|
|
||||||
this list of conditions and the following disclaimer in the documentation
|
|
||||||
and/or other materials provided with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of elm-decode-pipeline nor the names of its
|
|
||||||
contributors may be used to endorse or promote products derived from
|
|
||||||
this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
||||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
||||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
||||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
||||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
||||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
||||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
||||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
||||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
# elm-decode-pipeline
|
|
||||||
|
|
||||||
A library for building decoders using the pipeline [`(|>)`](http://package.elm-lang.org/packages/elm-lang/core/3.0.0/Basics#|>)
|
|
||||||
operator and plain function calls.
|
|
||||||
|
|
||||||
## Motivation
|
|
||||||
|
|
||||||
It's common to decode into a record that has a `type alias`. Here's an example
|
|
||||||
of this from the [`object3`](http://package.elm-lang.org/packages/elm-lang/core/3.0.0/Json-Decode#object3)
|
|
||||||
docs:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
type alias Job = { name : String, id : Int, completed : Bool }
|
|
||||||
|
|
||||||
point : Decoder Job
|
|
||||||
point =
|
|
||||||
object3 Job
|
|
||||||
("name" := string)
|
|
||||||
("id" := int)
|
|
||||||
("completed" := bool)
|
|
||||||
```
|
|
||||||
|
|
||||||
This works because a record type alias can be called as a normal function. In
|
|
||||||
that case it accepts one argument for each field (in whatever order the fields
|
|
||||||
are declared in the type alias) and then returns an appropriate record built
|
|
||||||
with those arguments.
|
|
||||||
|
|
||||||
The `objectN` decoders are straightforward, but require manually changing N
|
|
||||||
whenever the field count changes. This library provides functions designed to
|
|
||||||
be used with the `|>` operator, with the goal of having decoders that are both
|
|
||||||
easy to read and easy to modify.
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
Here is a decoder built with this library.
|
|
||||||
|
|
||||||
```elm
|
|
||||||
import Json.Decode exposing (int, string, float, Decoder)
|
|
||||||
import Json.Decode.Pipeline exposing (decode, required, optional, hardcoded)
|
|
||||||
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, email : Maybe String
|
|
||||||
, name : String
|
|
||||||
, percentExcited : Float
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
decode User
|
|
||||||
|> required "id" int
|
|
||||||
|> required "email" (nullable string) -- `null` decodes to `Nothing`
|
|
||||||
|> optional "name" string "(fallback if name is `null` or not present)"
|
|
||||||
|> hardcoded 1.0
|
|
||||||
```
|
|
||||||
|
|
||||||
In this example:
|
|
||||||
|
|
||||||
* `decode` is a synonym for [`succeed`](http://package.elm-lang.org/packages/elm-lang/core/3.0.0/Json-Decode#succeed) (it just reads better here)
|
|
||||||
* `required "id" int` is similar to `("id" := int)`
|
|
||||||
* `optional` is like `required`, but if the field is either `null` or not present, decoding does not fail; instead it succeeds with the provided fallback value.
|
|
||||||
* `hardcoded` does not look at the provided JSON, and instead always decodes to the same value.
|
|
||||||
|
|
||||||
You could use this decoder as follows:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
Json.Decode.decodeString
|
|
||||||
userDecoder
|
|
||||||
"""
|
|
||||||
{"id": 123, "email": "sam@example.com", "name": "Sam Sample"}
|
|
||||||
"""
|
|
||||||
```
|
|
||||||
|
|
||||||
The result would be:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
{ id = 123
|
|
||||||
, email = "sam@example.com"
|
|
||||||
, name = "Sam Sample"
|
|
||||||
, percentExcited = 1.0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
Alternatively, you could use it like so:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
Json.Decode.decodeString
|
|
||||||
userDecoder
|
|
||||||
"""
|
|
||||||
{"id": 123, "email": "sam@example.com", "percentExcited": "(hardcoded)"}
|
|
||||||
"""
|
|
||||||
```
|
|
||||||
|
|
||||||
In this case, the result would be:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
{ id = 123
|
|
||||||
, email = "sam@example.com"
|
|
||||||
, name = "(fallback if name not present)"
|
|
||||||
, percentExcited = 1.0
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
---
|
|
||||||
[][team]
|
|
||||||
[team]: http://noredink.com/about/team
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "3.0.0",
|
|
||||||
"summary": "A pipeline-friendly library for building JSON decoders.",
|
|
||||||
"repository": "https://github.com/NoRedInk/elm-decode-pipeline.git",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"source-directories": [
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"exposed-modules": [
|
|
||||||
"Json.Decode.Pipeline"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"elm-lang/core": "5.0.0 <= v < 6.0.0"
|
|
||||||
},
|
|
||||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
module Example exposing (..)
|
|
||||||
|
|
||||||
import Json.Decode exposing (int, string, float, Decoder)
|
|
||||||
import Json.Decode.Pipeline exposing (decode, required, optional, hardcoded)
|
|
||||||
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, name : String
|
|
||||||
, percentExcited : Float
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
decode User
|
|
||||||
|> required "id" int
|
|
||||||
|> optional "name" string "(fallback if name not present)"
|
|
||||||
|> hardcoded 1.0
|
|
||||||
@@ -1,292 +0,0 @@
|
|||||||
module Json.Decode.Pipeline exposing (required, requiredAt, optional, optionalAt, resolve, decode, hardcoded, custom)
|
|
||||||
|
|
||||||
{-| # Json.Decode.Pipeline
|
|
||||||
|
|
||||||
Use the `(|>)` operator to build JSON decoders.
|
|
||||||
|
|
||||||
## Decoding fields
|
|
||||||
|
|
||||||
@docs required, requiredAt, optional, optionalAt, hardcoded, custom
|
|
||||||
|
|
||||||
## Beginning and ending pipelines
|
|
||||||
|
|
||||||
@docs decode, resolve
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Json.Decode as Decode exposing (Decoder)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a required field.
|
|
||||||
|
|
||||||
import Json.Decode exposing (int, string, Decoder)
|
|
||||||
import Decode.Pipeline exposing (decode, required)
|
|
||||||
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, name : String
|
|
||||||
, email : String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
decode User
|
|
||||||
|> required "id" int
|
|
||||||
|> required "name" string
|
|
||||||
|> required "email" string
|
|
||||||
|
|
||||||
|
|
||||||
result : Result String User
|
|
||||||
result =
|
|
||||||
Decode.decodeString
|
|
||||||
userDecoder
|
|
||||||
"""
|
|
||||||
{"id": 123, "email": "sam@example.com", "name": "Sam"}
|
|
||||||
"""
|
|
||||||
-- Ok { id = 123, name = "Sam", email = "sam@example.com" }
|
|
||||||
-}
|
|
||||||
required : String -> Decoder a -> Decoder (a -> b) -> Decoder b
|
|
||||||
required key valDecoder decoder =
|
|
||||||
custom (Decode.field key valDecoder) decoder
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a required nested field.
|
|
||||||
-}
|
|
||||||
requiredAt : List String -> Decoder a -> Decoder (a -> b) -> Decoder b
|
|
||||||
requiredAt path valDecoder decoder =
|
|
||||||
custom (Decode.at path valDecoder) decoder
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a field that may be missing or have a null value. If the field is
|
|
||||||
missing, then it decodes as the `fallback` value. If the field is present,
|
|
||||||
then `valDecoder` is used to decode its value. If `valDecoder` fails on a
|
|
||||||
`null` value, then the `fallback` is used as if the field were missing
|
|
||||||
entirely.
|
|
||||||
|
|
||||||
import Json.Decode exposing (int, string, null, oneOf, Decoder)
|
|
||||||
import Decode.Pipeline exposing (decode, required, optional)
|
|
||||||
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, name : String
|
|
||||||
, email : String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
decode User
|
|
||||||
|> required "id" int
|
|
||||||
|> optional "name" string "blah"
|
|
||||||
|> required "email" string
|
|
||||||
|
|
||||||
|
|
||||||
result : Result String User
|
|
||||||
result =
|
|
||||||
Decode.decodeString
|
|
||||||
userDecoder
|
|
||||||
"""
|
|
||||||
{"id": 123, "email": "sam@example.com" }
|
|
||||||
"""
|
|
||||||
-- Ok { id = 123, name = "blah", email = "sam@example.com" }
|
|
||||||
|
|
||||||
Because `valDecoder` is given an opportunity to decode `null` values before
|
|
||||||
resorting to the `fallback`, you can distinguish between missing and `null`
|
|
||||||
values if you need to:
|
|
||||||
|
|
||||||
userDecoder2 =
|
|
||||||
decode User
|
|
||||||
|> required "id" int
|
|
||||||
|> optional "name" (oneOf [ string, null "NULL" ]) "MISSING"
|
|
||||||
|> required "email" string
|
|
||||||
|
|
||||||
-}
|
|
||||||
optional : String -> Decoder a -> a -> Decoder (a -> b) -> Decoder b
|
|
||||||
optional key valDecoder fallback decoder =
|
|
||||||
custom (optionalDecoder (Decode.field key Decode.value) valDecoder fallback) decoder
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode an optional nested field.
|
|
||||||
-}
|
|
||||||
optionalAt : List String -> Decoder a -> a -> Decoder (a -> b) -> Decoder b
|
|
||||||
optionalAt path valDecoder fallback decoder =
|
|
||||||
custom (optionalDecoder (Decode.at path Decode.value) valDecoder fallback) decoder
|
|
||||||
|
|
||||||
|
|
||||||
optionalDecoder : Decoder Decode.Value -> Decoder a -> a -> Decoder a
|
|
||||||
optionalDecoder pathDecoder valDecoder fallback =
|
|
||||||
let
|
|
||||||
nullOr decoder =
|
|
||||||
Decode.oneOf [ decoder, Decode.null fallback ]
|
|
||||||
|
|
||||||
handleResult input =
|
|
||||||
case Decode.decodeValue pathDecoder input of
|
|
||||||
Ok rawValue ->
|
|
||||||
-- The field was present, so now let's try to decode that value.
|
|
||||||
-- (If it was present but fails to decode, this should and will fail!)
|
|
||||||
case Decode.decodeValue (nullOr valDecoder) rawValue of
|
|
||||||
Ok finalResult ->
|
|
||||||
Decode.succeed finalResult
|
|
||||||
|
|
||||||
Err finalErr ->
|
|
||||||
Decode.fail finalErr
|
|
||||||
|
|
||||||
Err _ ->
|
|
||||||
-- The field was not present, so use the fallback.
|
|
||||||
Decode.succeed fallback
|
|
||||||
in
|
|
||||||
Decode.value
|
|
||||||
|> Decode.andThen handleResult
|
|
||||||
|
|
||||||
|
|
||||||
{-| Rather than decoding anything, use a fixed value for the next step in the
|
|
||||||
pipeline. `harcoded` does not look at the JSON at all.
|
|
||||||
|
|
||||||
import Json.Decode exposing (int, string, Decoder)
|
|
||||||
import Decode.Pipeline exposing (decode, required)
|
|
||||||
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, email : String
|
|
||||||
, followers : Int
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
decode User
|
|
||||||
|> required "id" int
|
|
||||||
|> required "email" string
|
|
||||||
|> hardcoded 0
|
|
||||||
|
|
||||||
|
|
||||||
result : Result String User
|
|
||||||
result =
|
|
||||||
Decode.decodeString
|
|
||||||
userDecoder
|
|
||||||
"""
|
|
||||||
{"id": 123, "email": "sam@example.com"}
|
|
||||||
"""
|
|
||||||
-- Ok { id = 123, email = "sam@example.com", followers = 0 }
|
|
||||||
-}
|
|
||||||
hardcoded : a -> Decoder (a -> b) -> Decoder b
|
|
||||||
hardcoded =
|
|
||||||
Decode.succeed >> custom
|
|
||||||
|
|
||||||
|
|
||||||
{-| Run the given decoder and feed its result into the pipeline at this point.
|
|
||||||
|
|
||||||
Consider this example.
|
|
||||||
|
|
||||||
import Json.Decode exposing (int, string, at, Decoder)
|
|
||||||
import Decode.Pipeline exposing (decode, required, custom)
|
|
||||||
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, name : String
|
|
||||||
, email : String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
decode User
|
|
||||||
|> required "id" int
|
|
||||||
|> custom (at [ "profile", "name" ] string)
|
|
||||||
|> required "email" string
|
|
||||||
|
|
||||||
|
|
||||||
result : Result String User
|
|
||||||
result =
|
|
||||||
Decode.decodeString
|
|
||||||
userDecoder
|
|
||||||
"""
|
|
||||||
{
|
|
||||||
"id": 123,
|
|
||||||
"email": "sam@example.com",
|
|
||||||
"profile": {"name": "Sam"}
|
|
||||||
}
|
|
||||||
"""
|
|
||||||
-- Ok { id = 123, name = "Sam", email = "sam@example.com" }
|
|
||||||
-}
|
|
||||||
custom : Decoder a -> Decoder (a -> b) -> Decoder b
|
|
||||||
custom =
|
|
||||||
Decode.map2 (|>)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a `Decoder (Result x a)` into a `Decoder a`. Useful when you want
|
|
||||||
to perform some custom processing just before completing the decoding operation.
|
|
||||||
|
|
||||||
import Json.Decode exposing (int, string, float, Decoder)
|
|
||||||
import Decode.Pipeline exposing
|
|
||||||
(decode, required, resolve)
|
|
||||||
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, email : String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
let
|
|
||||||
-- toDecoder gets run *after* all the
|
|
||||||
-- (|> required ...) steps are done.
|
|
||||||
toDecoder : Int -> String -> Int -> Decoder User
|
|
||||||
toDecoder id email version =
|
|
||||||
if version > 2 then
|
|
||||||
succeed (User id email)
|
|
||||||
else
|
|
||||||
fail "This JSON is from a deprecated source. Please upgrade!"
|
|
||||||
in
|
|
||||||
decode toDecoder
|
|
||||||
|> required "id" int
|
|
||||||
|> required "email" string
|
|
||||||
|> required "version" int -- version is part of toDecoder,
|
|
||||||
|> resolve -- but it is not a part of User
|
|
||||||
|
|
||||||
|
|
||||||
result : Result String User
|
|
||||||
result =
|
|
||||||
Decode.decodeString
|
|
||||||
userDecoder
|
|
||||||
"""
|
|
||||||
{"id": 123, "email": "sam@example.com", "version": 1}
|
|
||||||
"""
|
|
||||||
-- Err "This JSON is from a deprecated source. Please upgrade!"
|
|
||||||
-}
|
|
||||||
resolve : Decoder (Decoder a) -> Decoder a
|
|
||||||
resolve =
|
|
||||||
Decode.andThen identity
|
|
||||||
|
|
||||||
|
|
||||||
{-| Begin a decoding pipeline. This is a synonym for [Json.Decode.succeed](http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode#succeed),
|
|
||||||
intended to make things read more clearly.
|
|
||||||
|
|
||||||
import Json.Decode exposing (int, string, float, Decoder)
|
|
||||||
import Json.Decode.Pipeline exposing (decode, required, optional)
|
|
||||||
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, email : String
|
|
||||||
, name : String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
decode User
|
|
||||||
|> required "id" int
|
|
||||||
|> required "email" string
|
|
||||||
|> optional "name" string ""
|
|
||||||
-}
|
|
||||||
decode : a -> Decoder a
|
|
||||||
decode =
|
|
||||||
Decode.succeed
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/elm-stuff/
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
port module Main exposing (..)
|
|
||||||
|
|
||||||
{-|
|
|
||||||
Run the tests with node-test-runner:
|
|
||||||
|
|
||||||
https://github.com/rtfeldman/node-test-runner
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Tests
|
|
||||||
import Test.Runner.Node exposing (run)
|
|
||||||
import Json.Encode exposing (Value)
|
|
||||||
|
|
||||||
|
|
||||||
main : Program Never
|
|
||||||
main =
|
|
||||||
run emit Tests.all
|
|
||||||
|
|
||||||
|
|
||||||
port emit : ( String, Value ) -> Cmd msg
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
module Tests exposing (..)
|
|
||||||
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect exposing (Expectation)
|
|
||||||
import Json.Decode.Pipeline
|
|
||||||
exposing
|
|
||||||
( decode
|
|
||||||
, required
|
|
||||||
, requiredAt
|
|
||||||
, optional
|
|
||||||
, optionalAt
|
|
||||||
, resolveResult
|
|
||||||
)
|
|
||||||
import Json.Decode exposing (Decoder, string, null)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Run some JSON through a Decoder and return the result.
|
|
||||||
-}
|
|
||||||
runWith : String -> Decoder a -> Result String a
|
|
||||||
runWith =
|
|
||||||
flip Json.Decode.decodeString
|
|
||||||
|
|
||||||
|
|
||||||
isError : Result err ok -> Bool
|
|
||||||
isError result =
|
|
||||||
case result of
|
|
||||||
Err _ ->
|
|
||||||
True
|
|
||||||
|
|
||||||
Ok _ ->
|
|
||||||
False
|
|
||||||
|
|
||||||
|
|
||||||
expectErr : Result err ok -> Expectation
|
|
||||||
expectErr result =
|
|
||||||
isError result
|
|
||||||
|> Expect.true ("Expected an Err but got " ++ toString result)
|
|
||||||
|
|
||||||
|
|
||||||
all : Test
|
|
||||||
all =
|
|
||||||
describe
|
|
||||||
"Json.Decode.Pipeline"
|
|
||||||
[ test "should decode basic example" <|
|
|
||||||
\() ->
|
|
||||||
decode (,)
|
|
||||||
|> required "a" string
|
|
||||||
|> required "b" string
|
|
||||||
|> runWith """{"a":"foo","b":"bar"}"""
|
|
||||||
|> Expect.equal (Ok ( "foo", "bar" ))
|
|
||||||
, test "should decode requiredAt fields" <|
|
|
||||||
\() ->
|
|
||||||
decode (,)
|
|
||||||
|> requiredAt [ "a" ] string
|
|
||||||
|> requiredAt [ "b", "c" ] string
|
|
||||||
|> runWith """{"a":"foo","b":{"c":"bar"}}"""
|
|
||||||
|> Expect.equal (Ok ( "foo", "bar" ))
|
|
||||||
, test "should decode optionalAt fields" <|
|
|
||||||
\() ->
|
|
||||||
decode (,)
|
|
||||||
|> optionalAt [ "a", "b" ] string "--"
|
|
||||||
|> optionalAt [ "x", "y" ] string "--"
|
|
||||||
|> runWith """{"a":{},"x":{"y":"bar"}}"""
|
|
||||||
|> Expect.equal (Ok ( "--", "bar" ))
|
|
||||||
, test "optional succeeds if the field is not present" <|
|
|
||||||
\() ->
|
|
||||||
decode (,)
|
|
||||||
|> optional "a" string "--"
|
|
||||||
|> optional "x" string "--"
|
|
||||||
|> runWith """{"x":"five"}"""
|
|
||||||
|> Expect.equal (Ok ( "--", "five" ))
|
|
||||||
, test "optional succeeds with fallback if the field is present but null" <|
|
|
||||||
\() ->
|
|
||||||
decode (,)
|
|
||||||
|> optional "a" string "--"
|
|
||||||
|> optional "x" string "--"
|
|
||||||
|> runWith """{"a":null,"x":"five"}"""
|
|
||||||
|> Expect.equal (Ok ( "--", "five" ))
|
|
||||||
, test "optional succeeds with result of the given decoder if the field is null and the decoder decodes nulls" <|
|
|
||||||
\() ->
|
|
||||||
decode (,)
|
|
||||||
|> optional "a" (null "null") "--"
|
|
||||||
|> optional "x" string "--"
|
|
||||||
|> runWith """{"a":null,"x":"five"}"""
|
|
||||||
|> Expect.equal (Ok ( "null", "five" ))
|
|
||||||
, test "optional fails if the field is present but doesn't decode" <|
|
|
||||||
\() ->
|
|
||||||
decode (,)
|
|
||||||
|> optional "a" string "--"
|
|
||||||
|> optional "x" string "--"
|
|
||||||
|> runWith """{"x":5}"""
|
|
||||||
|> expectErr
|
|
||||||
, test "optionalAt fails if the field is present but doesn't decode" <|
|
|
||||||
\() ->
|
|
||||||
decode (,)
|
|
||||||
|> optionalAt [ "a", "b" ] string "--"
|
|
||||||
|> optionalAt [ "x", "y" ] string "--"
|
|
||||||
|> runWith """{"a":{},"x":{"y":5}}"""
|
|
||||||
|> expectErr
|
|
||||||
, test "resolveResult bubbles up decoded Err results" <|
|
|
||||||
\() ->
|
|
||||||
decode Err
|
|
||||||
|> required "error" string
|
|
||||||
|> resolveResult
|
|
||||||
|> runWith """{"error":"invalid"}"""
|
|
||||||
|> expectErr
|
|
||||||
, test "resolveResult bubbles up decoded Ok results" <|
|
|
||||||
\() ->
|
|
||||||
decode Ok
|
|
||||||
|> required "ok" string
|
|
||||||
|> resolveResult
|
|
||||||
|> runWith """{"ok":"valid"}"""
|
|
||||||
|> Expect.equal (Ok "valid")
|
|
||||||
]
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "1.0.0",
|
|
||||||
"summary": "Sample Elm Test",
|
|
||||||
"repository": "https://github.com/user/project.git",
|
|
||||||
"license": "BSD-3-Clause",
|
|
||||||
"source-directories": [
|
|
||||||
".",
|
|
||||||
"../src"
|
|
||||||
],
|
|
||||||
"exposed-modules": [],
|
|
||||||
"dependencies": {
|
|
||||||
"elm-lang/core": "5.0.0 <= v < 6.0.0",
|
|
||||||
"elm-community/elm-test": "2.0.0 <= v < 3.0.0",
|
|
||||||
"rtfeldman/node-test-runner": "1.0.0 <= v < 2.0.0"
|
|
||||||
},
|
|
||||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
# Ignore build or dist files
|
|
||||||
elm-stuff
|
|
||||||
node_modules
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
language: node_js
|
|
||||||
node_js: "node"
|
|
||||||
os: linux
|
|
||||||
env: ELM_VERSION=0.18.0
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- test/elm-stuff/build-artifacts
|
|
||||||
- sysconfcpus
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
|
|
||||||
- | # epic build time improvement - see https://github.com/elm-lang/elm-compiler/issues/1473#issuecomment-245704142
|
|
||||||
if [ ! -d sysconfcpus/bin ];
|
|
||||||
then
|
|
||||||
git clone https://github.com/obmarg/libsysconfcpus.git;
|
|
||||||
cd libsysconfcpus;
|
|
||||||
./configure --prefix=$TRAVIS_BUILD_DIR/sysconfcpus;
|
|
||||||
make && make install;
|
|
||||||
cd ..;
|
|
||||||
fi
|
|
||||||
|
|
||||||
install:
|
|
||||||
- node --version
|
|
||||||
- npm --version
|
|
||||||
- cd tests
|
|
||||||
- npm install -g elm@$ELM_VERSION
|
|
||||||
- mv $(npm config get prefix)/bin/elm-make $(npm config get prefix)/bin/elm-make-old
|
|
||||||
- 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
|
|
||||||
- chmod +x $(npm config get prefix)/bin/elm-make
|
|
||||||
- npm install
|
|
||||||
- elm package install --yes
|
|
||||||
|
|
||||||
script:
|
|
||||||
- npm test
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
### 2.6.0
|
|
||||||
|
|
||||||
**Additions:**
|
|
||||||
- `keys` allows extracting _only_ the keys from a JSON object
|
|
||||||
|
|
||||||
### 2.5.0
|
|
||||||
|
|
||||||
**Additions:**
|
|
||||||
- `dict` helps encoding `Dict`
|
|
||||||
|
|
||||||
### 2.4.0
|
|
||||||
|
|
||||||
**Additions:**
|
|
||||||
- `collection` helps with decoding array-like JavaScript structures such as `HTMLCollection`
|
|
||||||
- `combine` helps combining a `List` of decoders into a single `Decoder` for a `List` of such things
|
|
||||||
|
|
||||||
### 2.3.0
|
|
||||||
|
|
||||||
**Additions:**
|
|
||||||
- `indexedList` to get access to the current js array index while decoding
|
|
||||||
|
|
||||||
**Other Stuff:**
|
|
||||||
- `elm-doc-test` is now `elm-verify-examples`!
|
|
||||||
|
|
||||||
### 2.2.0
|
|
||||||
|
|
||||||
**Additions:**
|
|
||||||
- `parseInt` and `parseFloat` for weird api's that return numbers as strings
|
|
||||||
- `doubleEncoded` for a more generic _json as a string in json_ issues
|
|
||||||
|
|
||||||
**Fixes:**
|
|
||||||
- `optionalField` decodes the field, rather than the surrounding object now.
|
|
||||||
|
|
||||||
**Other Stuff:**
|
|
||||||
- Code Style conforms to elm-format@exp
|
|
||||||
- Doc tests!
|
|
||||||
- Travis integration
|
|
||||||
|
|
||||||
### 2.1.0
|
|
||||||
|
|
||||||
**Additions:**
|
|
||||||
- `optionalField : String -> Json.Decode.Decoder a -> Json.Decode.Decoder (Maybe.Maybe a)` - Decode an optional field, succeeding with `Nothing` if it is missing, but still giving an error if it is malformed.
|
|
||||||
|
|
||||||
### 2.0.0
|
|
||||||
|
|
||||||
**Breaking Changes:**
|
|
||||||
- Upgrade for Elm 0.18
|
|
||||||
- Removed `maybeNull` in favor of `Json.Decode.nullable`
|
|
||||||
- Removed `lazy` in favor of `Json.Decode.lazy`
|
|
||||||
- Renamed `apply` to `andMap` and reversed arguments to `Decoder a -> Decoder (a -> b) -> Decoder b` to make it work nicely with `(|>)`
|
|
||||||
|
|
||||||
**Additions:**
|
|
||||||
- `fromResult : Result String a -> Decoder a` - convert a `Result` to a `Decoder`, helpful in `andThen` callbacks following the removal of `Json.Decode.customDecoder`
|
|
||||||
- `Json.Encode.Extra.maybe : (a -> Value) -> Maybe a -> Value` - encode a `Maybe a` given an encoder for `a`. Thanks to @hendore for this addition.
|
|
||||||
|
|
||||||
**Other Stuff:**
|
|
||||||
- Code style conforms to elm-format
|
|
||||||
|
|
||||||
#### 1.1.0
|
|
||||||
|
|
||||||
**Additions:**
|
|
||||||
- `Json.Decode.Extra.sequence` - lets you generate a list of `Decoder a` and attempt to apply them to a JSON list. _Authored by @cobalamin_
|
|
||||||
|
|
||||||
|
|
||||||
#### 1.0.0
|
|
||||||
|
|
||||||
**Breaking Changes:**
|
|
||||||
- Upgrade for Elm 0.17
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
The MIT License (MIT)
|
|
||||||
|
|
||||||
Copyright (c) 2016 CircuitHub Inc., Elm Community members
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
||||||
copies of the Software, and to permit persons to whom the Software is
|
|
||||||
furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
||||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
||||||
SOFTWARE.
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
[](https://travis-ci.org/elm-community/json-extra)
|
|
||||||
|
|
||||||
# json-extra
|
|
||||||
|
|
||||||
```
|
|
||||||
elm-package install elm-community/json-extra
|
|
||||||
```
|
|
||||||
|
|
||||||
Convenience functions for working with JSON
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
## Json.Decode.Extra.andMap
|
|
||||||
|
|
||||||
Imagine you have a data type for a user
|
|
||||||
|
|
||||||
```elm
|
|
||||||
import Date (Date)
|
|
||||||
|
|
||||||
type alias User =
|
|
||||||
{ id : Int
|
|
||||||
, createdAt : Date
|
|
||||||
, updatedAt : Date
|
|
||||||
, deletedAt : Maybe Date
|
|
||||||
, username : Maybe String
|
|
||||||
, email : Maybe String
|
|
||||||
, isAdmin : Bool
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
You can use `andMap` to incrementally apply decoders to your `User` type alias
|
|
||||||
by using that type alias as a function. Recall that record type aliases are
|
|
||||||
also functions which accept arguments in the order their fields are declared. In
|
|
||||||
this case, `User` looks like
|
|
||||||
|
|
||||||
```elm
|
|
||||||
User : Int -> Date -> Date -> Maybe Date -> Maybe String -> Maybe String -> Bool -> User
|
|
||||||
```
|
|
||||||
|
|
||||||
And also recall that Elm functions can be partially applied. We can use these
|
|
||||||
properties to apply each field of our JSON object to each field in our user one
|
|
||||||
field at a time. All we need to do is also wrap `User` in a decoder and step
|
|
||||||
through using `andMap`.
|
|
||||||
|
|
||||||
```elm
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
succeed User
|
|
||||||
|> andMap (field "id" int)
|
|
||||||
|> andMap (field "createdAt" date)
|
|
||||||
|> andMap (field "updatedAt" date)
|
|
||||||
|> andMap (field "deletedAt" (maybe date))
|
|
||||||
|> andMap (field "username" (maybe string))
|
|
||||||
|> andMap (field "email" (maybe string))
|
|
||||||
|> andMap (field "isAdmin" bool)
|
|
||||||
```
|
|
||||||
|
|
||||||
This is a shortened form of
|
|
||||||
|
|
||||||
```elm
|
|
||||||
userDecoder : Decoder User
|
|
||||||
userDecoder =
|
|
||||||
succeed User
|
|
||||||
|> andThen (\f -> map f (field "id" int))
|
|
||||||
|> andThen (\f -> map f (field "createdAt" date))
|
|
||||||
|> andThen (\f -> map f (field "updatedAt" date))
|
|
||||||
|> andThen (\f -> map f (field "deletedAt" (maybe date)))
|
|
||||||
|> andThen (\f -> map f (field "username" (maybe string)))
|
|
||||||
|> andThen (\f -> map f (field "email" (maybe string)))
|
|
||||||
|> andThen (\f -> map f (field "isAdmin" bool))
|
|
||||||
```
|
|
||||||
|
|
||||||
See also: The [docs for `(|:)`](https://github.com/elm-community/json-extra/blob/master/docs/infixAndMap.md)
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
## Json.Decode.Extra.(|:)
|
|
||||||
|
|
||||||
|
|
||||||
Infix version of `andMap` that makes for a nice DSL when decoding objects.
|
|
||||||
|
|
||||||
Consider the following type alias for a `Location`:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
type alias Location =
|
|
||||||
{ id : Int
|
|
||||||
, name : String
|
|
||||||
, address : String
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
We can use `(|:)` to build up a decoder for `Location`:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
locationDecoder : Decoder Location
|
|
||||||
locationDecoder =
|
|
||||||
succeed Location
|
|
||||||
|: (field "id" int)
|
|
||||||
|: (field "name" string)
|
|
||||||
|: (field "address" string)
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
If you're curious, here's how this works behind the scenes, read on.
|
|
||||||
|
|
||||||
`Location` is a type alias, and type aliases give you a convenience function
|
|
||||||
that returns an instance of the record in question. Try this out in `elm-repl`:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
> type alias Location = { id : Int, name: String, address: String }
|
|
||||||
|
|
||||||
> Location
|
|
||||||
<function> : Int -> String -> String -> Repl.Location
|
|
||||||
|
|
||||||
> Location 1 "The White House" "1600 Pennsylvania Ave"
|
|
||||||
{ id = 1, name = "The White House", address = "1600 Pennsylvania Ave" }
|
|
||||||
```
|
|
||||||
|
|
||||||
In other words, if you call the `Location` function, passing three arguments,
|
|
||||||
it will return a new `Location` record by filling in each of its fields. (The
|
|
||||||
argument order is based on the order in which we listed the fields in the
|
|
||||||
type alias; the first argument sets `id`, the second argument sets `name`, etc.)
|
|
||||||
|
|
||||||
Now try running this through `elm-repl`:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
> import Json.Decode exposing (succeed, int, string, field)
|
|
||||||
|
|
||||||
> succeed Location
|
|
||||||
<function>
|
|
||||||
: Json.Decode.Decoder
|
|
||||||
(Int -> String -> String -> Repl.Location)
|
|
||||||
```
|
|
||||||
|
|
||||||
So `succeed Location` gives us a `Decoder (Int -> String -> String -> Location)`.
|
|
||||||
That's not what we want! What we want is a `Decoder Location`. All we have so
|
|
||||||
far is a `Decoder` that wraps not a `Location`, but rather a function that
|
|
||||||
returns a `Location`.
|
|
||||||
|
|
||||||
What `|: (field "id" int)` does is to take that wrapped function and pass an
|
|
||||||
argument to it.
|
|
||||||
|
|
||||||
```elm
|
|
||||||
> import Json.Decode exposing (succeed, int, string, field)
|
|
||||||
|
|
||||||
> (field "id" int)
|
|
||||||
<function> : Json.Decode.Decoder Int
|
|
||||||
|
|
||||||
> succeed Location |: (field "id" int)
|
|
||||||
<function>
|
|
||||||
: Json.Decode.Decoder
|
|
||||||
(String -> String -> Repl.Location)
|
|
||||||
```
|
|
||||||
|
|
||||||
Notice how the wrapped function no longer takes an `Int` as its first argument.
|
|
||||||
That's because `(|:)` went ahead and supplied one: the `Int` wrapped by the decoder
|
|
||||||
`(field "id" int)` (which returns a `Decoder Int`).
|
|
||||||
|
|
||||||
Compare:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
> succeed Location
|
|
||||||
Decoder (Int -> String -> String -> Location)
|
|
||||||
|
|
||||||
> succeed Location |: (field "id" int)
|
|
||||||
Decoder (String -> String -> Location)
|
|
||||||
```
|
|
||||||
|
|
||||||
We still want a `Decoder Location` and we still don't have it yet. Our decoder
|
|
||||||
still wraps a function instead of a plain `Location`. However, that function is
|
|
||||||
now smaller by one argument!
|
|
||||||
|
|
||||||
Let's repeat this pattern to provide the first `String` argument next.
|
|
||||||
|
|
||||||
```elm
|
|
||||||
> succeed Location
|
|
||||||
Decoder (Int -> String -> String -> Location)
|
|
||||||
|
|
||||||
> succeed Location |: (field "id" int)
|
|
||||||
Decoder (String -> String -> Location)
|
|
||||||
|
|
||||||
> succeed Location |: (field "id" int) |: (field "name" string)
|
|
||||||
Decoder (String -> Location)
|
|
||||||
```
|
|
||||||
|
|
||||||
Smaller and smaller! Now we're down from `(Int -> String -> String -> Location)`
|
|
||||||
to `(String -> Location)`. What happens if we repeat the pattern one more time?
|
|
||||||
|
|
||||||
```elm
|
|
||||||
> succeed Location
|
|
||||||
Decoder (Int -> String -> String -> Location)
|
|
||||||
|
|
||||||
> succeed Location |: (field "id" int)
|
|
||||||
Decoder (String -> String -> Location)
|
|
||||||
|
|
||||||
> succeed Location |: (field "id" int) |: (field "name" string)
|
|
||||||
Decoder (String -> Location)
|
|
||||||
|
|
||||||
> succeed Location |: (field "id" int) |: (field "name" string) |: (field "address" string)
|
|
||||||
Decoder Location
|
|
||||||
```
|
|
||||||
|
|
||||||
Having now supplied all three arguments to the wrapped function, it has ceased
|
|
||||||
to be a function. It's now just a plain old `Location`, like we wanted all along.
|
|
||||||
|
|
||||||
We win!
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "2.7.0",
|
|
||||||
"summary": "Convenience functions for working with Json",
|
|
||||||
"repository": "https://github.com/elm-community/json-extra.git",
|
|
||||||
"license": "MIT",
|
|
||||||
"source-directories": [
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"exposed-modules": [
|
|
||||||
"Json.Decode.Extra",
|
|
||||||
"Json.Encode.Extra"
|
|
||||||
],
|
|
||||||
"dependencies": {
|
|
||||||
"elm-lang/core": "5.0.0 <= v < 6.0.0"
|
|
||||||
},
|
|
||||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
|
||||||
}
|
|
||||||
2903
finished/elm-stuff/packages/elm-community/json-extra/2.7.0/package-lock.json
generated
vendored
2903
finished/elm-stuff/packages/elm-community/json-extra/2.7.0/package-lock.json
generated
vendored
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"test": "elm-verify-examples && elm-format --validate src"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"elm-format": "^0.7.0-exp",
|
|
||||||
"elm-verify-examples": "^2.3.1"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,565 +0,0 @@
|
|||||||
module Json.Decode.Extra
|
|
||||||
exposing
|
|
||||||
( (|:)
|
|
||||||
, andMap
|
|
||||||
, collection
|
|
||||||
, combine
|
|
||||||
, date
|
|
||||||
, dict2
|
|
||||||
, doubleEncoded
|
|
||||||
, fromResult
|
|
||||||
, indexedList
|
|
||||||
, keys
|
|
||||||
, optionalField
|
|
||||||
, parseFloat
|
|
||||||
, parseInt
|
|
||||||
, sequence
|
|
||||||
, set
|
|
||||||
, when
|
|
||||||
, withDefault
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Convenience functions for working with Json
|
|
||||||
|
|
||||||
|
|
||||||
# Date
|
|
||||||
|
|
||||||
@docs date
|
|
||||||
|
|
||||||
|
|
||||||
# Incremental Decoding
|
|
||||||
|
|
||||||
@docs andMap, (|:)
|
|
||||||
|
|
||||||
|
|
||||||
# Conditional Decoding
|
|
||||||
|
|
||||||
@docs when
|
|
||||||
|
|
||||||
|
|
||||||
# List
|
|
||||||
|
|
||||||
@docs collection, sequence, combine, indexedList, keys
|
|
||||||
|
|
||||||
|
|
||||||
# Set
|
|
||||||
|
|
||||||
@docs set
|
|
||||||
|
|
||||||
|
|
||||||
# Dict
|
|
||||||
|
|
||||||
@docs dict2
|
|
||||||
|
|
||||||
|
|
||||||
# Maybe
|
|
||||||
|
|
||||||
@docs withDefault, optionalField
|
|
||||||
|
|
||||||
|
|
||||||
# Result
|
|
||||||
|
|
||||||
@docs fromResult
|
|
||||||
|
|
||||||
|
|
||||||
# Encoded strings
|
|
||||||
|
|
||||||
@docs parseInt, parseFloat, doubleEncoded
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Date
|
|
||||||
import Dict exposing (Dict)
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
import Set exposing (Set)
|
|
||||||
import String
|
|
||||||
|
|
||||||
|
|
||||||
{-| Can be helpful when decoding large objects incrementally.
|
|
||||||
|
|
||||||
See [the `andMap` docs](https://github.com/elm-community/json-extra/blob/2.0.0/docs/andMap.md)
|
|
||||||
for an explanation of how `andMap` works and how to use it.
|
|
||||||
|
|
||||||
-}
|
|
||||||
andMap : Decoder a -> Decoder (a -> b) -> Decoder b
|
|
||||||
andMap =
|
|
||||||
map2 (|>)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Infix version of `andMap` that makes for a nice DSL when decoding objects.
|
|
||||||
|
|
||||||
See [the `(|:)` docs](https://github.com/elm-community/json-extra/blob/2.0.0/docs/infixAndMap.md)
|
|
||||||
for an explanation of how `(|:)` works and how to use it.
|
|
||||||
|
|
||||||
-}
|
|
||||||
(|:) : Decoder (a -> b) -> Decoder a -> Decoder b
|
|
||||||
(|:) =
|
|
||||||
flip andMap
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract a date using [`Date.fromString`](http://package.elm-lang.org/packages/elm-lang/core/latest/Date#fromString)
|
|
||||||
|
|
||||||
import Date
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
""" "2012-04-23T18:25:43.511Z" """
|
|
||||||
|> decodeString date
|
|
||||||
--> Date.fromString "2012-04-23T18:25:43.511Z"
|
|
||||||
|
|
||||||
|
|
||||||
""" "foo" """
|
|
||||||
|> decodeString date
|
|
||||||
--> Err "I ran into a `fail` decoder: Unable to parse 'foo' as a date. Dates must be in the ISO 8601 format."
|
|
||||||
|
|
||||||
-}
|
|
||||||
date : Decoder Date.Date
|
|
||||||
date =
|
|
||||||
string
|
|
||||||
|> andThen (Date.fromString >> fromResult)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract a set.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
import Set
|
|
||||||
|
|
||||||
|
|
||||||
"[ 1, 1, 5, 2 ]"
|
|
||||||
|> decodeString (set int)
|
|
||||||
--> Ok <| Set.fromList [ 1, 2, 5 ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
set : Decoder comparable -> Decoder (Set comparable)
|
|
||||||
set decoder =
|
|
||||||
list decoder
|
|
||||||
|> map Set.fromList
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract a dict using separate decoders for keys and values.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
import Dict
|
|
||||||
|
|
||||||
|
|
||||||
""" { "1": "foo", "2": "bar" } """
|
|
||||||
|> decodeString (dict2 int string)
|
|
||||||
--> Ok <| Dict.fromList [ ( 1, "foo" ), ( 2, "bar" ) ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
dict2 : Decoder comparable -> Decoder v -> Decoder (Dict comparable v)
|
|
||||||
dict2 keyDecoder valueDecoder =
|
|
||||||
keyValuePairs valueDecoder
|
|
||||||
|> andThen (decodeDictFromTuples keyDecoder)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Helper function for dict
|
|
||||||
-}
|
|
||||||
decodeDictFromTuples : Decoder comparable -> List ( String, v ) -> Decoder (Dict comparable v)
|
|
||||||
decodeDictFromTuples keyDecoder tuples =
|
|
||||||
case tuples of
|
|
||||||
[] ->
|
|
||||||
succeed Dict.empty
|
|
||||||
|
|
||||||
( strKey, value ) :: rest ->
|
|
||||||
case decodeString keyDecoder strKey of
|
|
||||||
Ok key ->
|
|
||||||
decodeDictFromTuples keyDecoder rest
|
|
||||||
|> andThen (Dict.insert key value >> succeed)
|
|
||||||
|
|
||||||
Err error ->
|
|
||||||
fail error
|
|
||||||
|
|
||||||
|
|
||||||
{-| Try running the given decoder; if that fails, then succeed with the given
|
|
||||||
fallback value.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
""" { "children": "oops" } """
|
|
||||||
|> decodeString (field "children" (list string) |> withDefault [])
|
|
||||||
--> Ok []
|
|
||||||
|
|
||||||
|
|
||||||
""" null """
|
|
||||||
|> decodeString (field "children" (list string) |> withDefault [])
|
|
||||||
--> Ok []
|
|
||||||
|
|
||||||
|
|
||||||
""" 30 """
|
|
||||||
|> decodeString (int |> withDefault 42)
|
|
||||||
--> Ok 30
|
|
||||||
|
|
||||||
-}
|
|
||||||
withDefault : a -> Decoder a -> Decoder a
|
|
||||||
withDefault fallback decoder =
|
|
||||||
maybe decoder
|
|
||||||
|> map (Maybe.withDefault fallback)
|
|
||||||
|
|
||||||
|
|
||||||
{-| If a field is missing, succeed with `Nothing`. If it is present, decode it
|
|
||||||
as normal and wrap successes in a `Just`.
|
|
||||||
|
|
||||||
When decoding with
|
|
||||||
[`maybe`](http://package.elm-lang.org/packages/elm-lang/core/latest/Json-Decode#maybe),
|
|
||||||
if a field is present but malformed, you get a success and Nothing.
|
|
||||||
`optionalField` gives you a failed decoding in that case, so you know
|
|
||||||
you received malformed data.
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
Let's define a `stuffDecoder` that extracts the `"stuff"` field, if it exists.
|
|
||||||
|
|
||||||
stuffDecoder : Decoder (Maybe String)
|
|
||||||
stuffDecoder =
|
|
||||||
optionalField "stuff" string
|
|
||||||
|
|
||||||
If the "stuff" field is missing, decode to Nothing.
|
|
||||||
|
|
||||||
""" { } """
|
|
||||||
|> decodeString stuffDecoder
|
|
||||||
--> Ok Nothing
|
|
||||||
|
|
||||||
If the "stuff" field is present but not a String, fail decoding.
|
|
||||||
|
|
||||||
""" { "stuff": [] } """
|
|
||||||
|> decodeString stuffDecoder
|
|
||||||
--> Err "Expecting a String at _.stuff but instead got: []"
|
|
||||||
|
|
||||||
If the "stuff" field is present and valid, decode to Just String.
|
|
||||||
|
|
||||||
""" { "stuff": "yay!" } """
|
|
||||||
|> decodeString stuffDecoder
|
|
||||||
--> Ok <| Just "yay!"
|
|
||||||
|
|
||||||
-}
|
|
||||||
optionalField : String -> Decoder a -> Decoder (Maybe a)
|
|
||||||
optionalField fieldName decoder =
|
|
||||||
let
|
|
||||||
finishDecoding json =
|
|
||||||
case decodeValue (field fieldName value) json of
|
|
||||||
Ok val ->
|
|
||||||
-- The field is present, so run the decoder on it.
|
|
||||||
map Just (field fieldName decoder)
|
|
||||||
|
|
||||||
Err _ ->
|
|
||||||
-- The field was missing, which is fine!
|
|
||||||
succeed Nothing
|
|
||||||
in
|
|
||||||
value
|
|
||||||
|> andThen finishDecoding
|
|
||||||
|
|
||||||
|
|
||||||
{-| This function turns a list of decoders into a decoder that returns a list.
|
|
||||||
|
|
||||||
The returned decoder will zip the list of decoders with a list of values,
|
|
||||||
matching each decoder with exactly one value at the same position. This is most
|
|
||||||
often useful in cases when you find yourself needing to dynamically generate a
|
|
||||||
list of decoders based on some data, and decode some other data with this list
|
|
||||||
of decoders.
|
|
||||||
|
|
||||||
Note that this function, unlike `List.map2`'s behaviour, expects the list of
|
|
||||||
decoders to have the same length as the list of values in the JSON.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
sequence
|
|
||||||
[ map Just string
|
|
||||||
, succeed Nothing
|
|
||||||
, map Just string
|
|
||||||
]
|
|
||||||
|> flip decodeString """ [ "pick me", "ignore me", "and pick me" ] """
|
|
||||||
--> Ok [ Just "pick me", Nothing, Just "and pick me" ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
sequence : List (Decoder a) -> Decoder (List a)
|
|
||||||
sequence decoders =
|
|
||||||
list value |> andThen (sequenceHelp decoders)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Helper function for sequence
|
|
||||||
-}
|
|
||||||
sequenceHelp : List (Decoder a) -> List Value -> Decoder (List a)
|
|
||||||
sequenceHelp decoders jsonValues =
|
|
||||||
if List.length jsonValues /= List.length decoders then
|
|
||||||
fail "Number of decoders does not match number of values"
|
|
||||||
else
|
|
||||||
List.map2 decodeValue decoders jsonValues
|
|
||||||
|> List.foldr (Result.map2 (::)) (Ok [])
|
|
||||||
|> fromResult
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get access to the current index while decoding a list element.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
repeatedStringDecoder : Int -> Decoder String
|
|
||||||
repeatedStringDecoder times =
|
|
||||||
string |> map (String.repeat times)
|
|
||||||
|
|
||||||
|
|
||||||
""" [ "a", "b", "c", "d" ] """
|
|
||||||
|> decodeString (indexedList repeatedStringDecoder)
|
|
||||||
--> Ok [ "", "b", "cc", "ddd" ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
indexedList : (Int -> Decoder a) -> Decoder (List a)
|
|
||||||
indexedList indexedDecoder =
|
|
||||||
list value
|
|
||||||
|> andThen
|
|
||||||
(\values ->
|
|
||||||
List.range 0 (List.length values - 1)
|
|
||||||
|> List.map indexedDecoder
|
|
||||||
|> sequence
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get a list of the keys of a JSON object
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
""" { "alice": 42, "bob": 99 } """
|
|
||||||
|> decodeString keys
|
|
||||||
--> Ok [ "alice", "bob" ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
keys : Decoder (List String)
|
|
||||||
keys =
|
|
||||||
keyValuePairs (succeed ())
|
|
||||||
|> map (List.foldl (\( key, _ ) acc -> key :: acc) [])
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform a result into a decoder
|
|
||||||
|
|
||||||
Sometimes it can be useful to use functions that primarily operate on
|
|
||||||
`Result` in decoders. An example of this is `Json.Decode.Extra.date`. It
|
|
||||||
uses the built-in `Date.fromString` to parse a `String` as a `Date`, and
|
|
||||||
then converts the `Result` from that conversion into a decoder which has
|
|
||||||
either already succeeded or failed based on the outcome.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
validateString : String -> Result String String
|
|
||||||
validateString input =
|
|
||||||
case input of
|
|
||||||
"" ->
|
|
||||||
Err "Empty string is not allowed"
|
|
||||||
_ ->
|
|
||||||
Ok input
|
|
||||||
|
|
||||||
|
|
||||||
""" "something" """
|
|
||||||
|> decodeString (string |> andThen (fromResult << validateString))
|
|
||||||
--> Ok "something"
|
|
||||||
|
|
||||||
|
|
||||||
""" "" """
|
|
||||||
|> decodeString (string |> andThen (fromResult << validateString))
|
|
||||||
--> Err "I ran into a `fail` decoder: Empty string is not allowed"
|
|
||||||
|
|
||||||
-}
|
|
||||||
fromResult : Result String a -> Decoder a
|
|
||||||
fromResult result =
|
|
||||||
case result of
|
|
||||||
Ok successValue ->
|
|
||||||
succeed successValue
|
|
||||||
|
|
||||||
Err errorMessage ->
|
|
||||||
fail errorMessage
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract an int using [`String.toInt`](http://package.elm-lang.org/packages/elm-lang/core/latest/String#toInt)
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
""" { "field": "123" } """
|
|
||||||
|> decodeString (field "field" parseInt)
|
|
||||||
--> Ok 123
|
|
||||||
|
|
||||||
-}
|
|
||||||
parseInt : Decoder Int
|
|
||||||
parseInt =
|
|
||||||
string |> andThen (String.toInt >> fromResult)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract a float using [`String.toFloat`](http://package.elm-lang.org/packages/elm-lang/core/latest/String#toFloat)
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
""" { "field": "50.5" } """
|
|
||||||
|> decodeString (field "field" parseFloat)
|
|
||||||
--> Ok 50.5
|
|
||||||
|
|
||||||
-}
|
|
||||||
parseFloat : Decoder Float
|
|
||||||
parseFloat =
|
|
||||||
string |> andThen (String.toFloat >> fromResult)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract a JSON-encoded string field
|
|
||||||
|
|
||||||
"Yo dawg, I heard you like JSON..."
|
|
||||||
|
|
||||||
If someone has put JSON in your JSON (perhaps a JSON log entry, encoded
|
|
||||||
as a string) this is the function you're looking for. Give it a decoder
|
|
||||||
and it will return a new decoder that applies your decoder to a string
|
|
||||||
field and yields the result (or fails if your decoder fails).
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
logEntriesDecoder : Decoder (List String)
|
|
||||||
logEntriesDecoder =
|
|
||||||
doubleEncoded (list string)
|
|
||||||
|
|
||||||
|
|
||||||
logsDecoder : Decoder (List String)
|
|
||||||
logsDecoder =
|
|
||||||
field "logs" logEntriesDecoder
|
|
||||||
|
|
||||||
|
|
||||||
""" { "logs": "[\\"log1\\", \\"log2\\"]"} """
|
|
||||||
|> decodeString logsDecoder
|
|
||||||
--> Ok [ "log1", "log2" ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
doubleEncoded : Decoder a -> Decoder a
|
|
||||||
doubleEncoded decoder =
|
|
||||||
string |> andThen (fromResult << decodeString decoder)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Helps converting a list of decoders into a decoder for a list of that type.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
decoders : List (Decoder String)
|
|
||||||
decoders =
|
|
||||||
[ field "foo" string
|
|
||||||
, field "bar" string
|
|
||||||
, field "another" string
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
""" { "foo": "hello", "another": "!", "bar": "world" } """
|
|
||||||
|> decodeString (combine decoders)
|
|
||||||
--> Ok [ "hello", "world", "!" ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
combine : List (Decoder a) -> Decoder (List a)
|
|
||||||
combine =
|
|
||||||
List.foldr (map2 (::)) (succeed [])
|
|
||||||
|
|
||||||
|
|
||||||
{-| Some JavaScript structures look like arrays, but aren't really. Examples
|
|
||||||
include `HTMLCollection`, `NodeList` and everything else that has a `length`
|
|
||||||
property, has values indexed by an integer key between 0 and `length`, but yet
|
|
||||||
_is not_ a JavaScript Array.
|
|
||||||
|
|
||||||
This decoder can come to the rescue.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
""" { "length": 3, "0": "foo", "1": "bar", "2": "baz" } """
|
|
||||||
|> decodeString (collection string)
|
|
||||||
--> Ok [ "foo", "bar", "baz" ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
collection : Decoder a -> Decoder (List a)
|
|
||||||
collection decoder =
|
|
||||||
field "length" int
|
|
||||||
|> andThen
|
|
||||||
(\length ->
|
|
||||||
List.range 0 (length - 1)
|
|
||||||
|> List.map (\index -> field (toString index) decoder)
|
|
||||||
|> combine
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Helper for conditionally decoding values based on some discriminator
|
|
||||||
that needs to pass a certain check.
|
|
||||||
|
|
||||||
import Json.Decode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
is : a -> a -> Bool
|
|
||||||
is a b =
|
|
||||||
a == b
|
|
||||||
|
|
||||||
|
|
||||||
enabledValue : Decoder Int
|
|
||||||
enabledValue =
|
|
||||||
(field "value" int)
|
|
||||||
|> when (field "enabled" bool) (is True)
|
|
||||||
|
|
||||||
|
|
||||||
""" { "enabled": true, "value": 123 } """
|
|
||||||
|> decodeString enabledValue
|
|
||||||
--> Ok 123
|
|
||||||
|
|
||||||
|
|
||||||
""" { "enabled": false, "value": 321 } """
|
|
||||||
|> decodeString enabledValue
|
|
||||||
--> Err "I ran into a `fail` decoder: Check failed with input `False`"
|
|
||||||
|
|
||||||
This can also be used to decode union types that are encoded with a discriminator field:
|
|
||||||
|
|
||||||
type Animal = Cat String | Dog String
|
|
||||||
|
|
||||||
|
|
||||||
dog : Decoder Animal
|
|
||||||
dog =
|
|
||||||
map Dog (field "name" string)
|
|
||||||
|
|
||||||
|
|
||||||
cat : Decoder Animal
|
|
||||||
cat =
|
|
||||||
map Cat (field "name" string)
|
|
||||||
|
|
||||||
|
|
||||||
animalType : Decoder String
|
|
||||||
animalType =
|
|
||||||
field "type" string
|
|
||||||
|
|
||||||
|
|
||||||
animal : Decoder Animal
|
|
||||||
animal =
|
|
||||||
oneOf
|
|
||||||
[ when animalType (is "dog") dog
|
|
||||||
, when animalType (is "cat") cat
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
"""
|
|
||||||
[
|
|
||||||
{ "type": "dog", "name": "Dawg" },
|
|
||||||
{ "type": "cat", "name": "Roxy" }
|
|
||||||
]
|
|
||||||
"""
|
|
||||||
|> decodeString (list animal)
|
|
||||||
--> Ok [ Dog "Dawg", Cat "Roxy" ]
|
|
||||||
|
|
||||||
-}
|
|
||||||
when : Decoder a -> (a -> Bool) -> Decoder b -> Decoder b
|
|
||||||
when checkDecoder check passDecoder =
|
|
||||||
checkDecoder
|
|
||||||
|> andThen
|
|
||||||
(\checkVal ->
|
|
||||||
if check checkVal then
|
|
||||||
passDecoder
|
|
||||||
else
|
|
||||||
fail <|
|
|
||||||
"Check failed with input `"
|
|
||||||
++ toString checkVal
|
|
||||||
++ "`"
|
|
||||||
)
|
|
||||||
@@ -1,47 +0,0 @@
|
|||||||
module Json.Encode.Extra exposing (dict, maybe)
|
|
||||||
|
|
||||||
{-| Convenience functions for turning Elm values into Json values.
|
|
||||||
|
|
||||||
@docs dict, maybe
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Dict exposing (Dict)
|
|
||||||
import Json.Encode exposing (Value, encode, int, null, object)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Encode a Maybe value. If the value is `Nothing` it will be encoded as `null`
|
|
||||||
|
|
||||||
import Json.Encode exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
maybe int (Just 50)
|
|
||||||
--> int 50
|
|
||||||
|
|
||||||
|
|
||||||
maybe int Nothing
|
|
||||||
--> null
|
|
||||||
|
|
||||||
-}
|
|
||||||
maybe : (a -> Value) -> Maybe a -> Value
|
|
||||||
maybe encoder =
|
|
||||||
Maybe.map encoder >> Maybe.withDefault null
|
|
||||||
|
|
||||||
|
|
||||||
{-| Turn a `Dict` into a JSON object.
|
|
||||||
|
|
||||||
import Json.Encode exposing (..)
|
|
||||||
import Dict
|
|
||||||
|
|
||||||
|
|
||||||
Dict.fromList [ ( "Sue", 38 ), ( "Tom", 42 ) ]
|
|
||||||
|> dict identity int
|
|
||||||
|> encode 0
|
|
||||||
--> """{"Sue":38,"Tom":42}"""
|
|
||||||
|
|
||||||
-}
|
|
||||||
dict : (comparable -> String) -> (v -> Value) -> Dict comparable v -> Value
|
|
||||||
dict toKey toValue dict =
|
|
||||||
Dict.toList dict
|
|
||||||
|> List.map (\( key, value ) -> ( toKey key, toValue value ))
|
|
||||||
|> object
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "1.0.0",
|
|
||||||
"summary": "Test Suites",
|
|
||||||
"repository": "https://github.com/elm-community/json-extra.git",
|
|
||||||
"license": "MIT",
|
|
||||||
"source-directories": [
|
|
||||||
"../src",
|
|
||||||
"."
|
|
||||||
],
|
|
||||||
"exposed-modules": [],
|
|
||||||
"dependencies": {
|
|
||||||
"elm-lang/core": "5.0.0 <= v < 6.0.0",
|
|
||||||
"elm-community/elm-test": "4.0.0 <= v < 5.0.0"
|
|
||||||
},
|
|
||||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"root": "../src",
|
|
||||||
"tests": [
|
|
||||||
"Json.Encode.Extra",
|
|
||||||
"Json.Decode.Extra"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,156 +0,0 @@
|
|||||||
{
|
|
||||||
"parser": "babel-eslint", // https://github.com/babel/babel-eslint
|
|
||||||
"plugins": [],
|
|
||||||
"env": { // http://eslint.org/docs/user-guide/configuring.html#specifying-environments
|
|
||||||
"browser": true, // browser global variables
|
|
||||||
"node": true // Node.js global variables and Node.js-specific rules
|
|
||||||
},
|
|
||||||
"ecmaFeatures": {
|
|
||||||
"arrowFunctions": true,
|
|
||||||
"blockBindings": true,
|
|
||||||
"classes": true,
|
|
||||||
"defaultParams": true,
|
|
||||||
"destructuring": true,
|
|
||||||
"forOf": true,
|
|
||||||
"generators": false,
|
|
||||||
"modules": true,
|
|
||||||
"objectLiteralComputedProperties": true,
|
|
||||||
"objectLiteralDuplicateProperties": false,
|
|
||||||
"objectLiteralShorthandMethods": true,
|
|
||||||
"objectLiteralShorthandProperties": true,
|
|
||||||
"spread": true,
|
|
||||||
"superInFunctions": true,
|
|
||||||
"templateStrings": true,
|
|
||||||
},
|
|
||||||
"rules": {
|
|
||||||
/**
|
|
||||||
* Strict mode
|
|
||||||
*/
|
|
||||||
// babel inserts "use strict"; for us
|
|
||||||
"strict": [2, "never"], // http://eslint.org/docs/rules/strict
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Variables
|
|
||||||
*/
|
|
||||||
"no-shadow": 2, // http://eslint.org/docs/rules/no-shadow
|
|
||||||
"no-shadow-restricted-names": 2, // http://eslint.org/docs/rules/no-shadow-restricted-names
|
|
||||||
"no-unused-vars": [2, { // http://eslint.org/docs/rules/no-unused-vars
|
|
||||||
"vars": "local",
|
|
||||||
"args": "after-used"
|
|
||||||
}],
|
|
||||||
"no-use-before-define": 2, // http://eslint.org/docs/rules/no-use-before-define
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Possible errors
|
|
||||||
*/
|
|
||||||
"no-cond-assign": [2, "always"], // http://eslint.org/docs/rules/no-cond-assign
|
|
||||||
"no-console": 1, // http://eslint.org/docs/rules/no-console
|
|
||||||
"no-debugger": 1, // http://eslint.org/docs/rules/no-debugger
|
|
||||||
"no-alert": 1, // http://eslint.org/docs/rules/no-alert
|
|
||||||
"no-constant-condition": 1, // http://eslint.org/docs/rules/no-constant-condition
|
|
||||||
"no-dupe-keys": 2, // http://eslint.org/docs/rules/no-dupe-keys
|
|
||||||
"no-duplicate-case": 2, // http://eslint.org/docs/rules/no-duplicate-case
|
|
||||||
"no-empty": 2, // http://eslint.org/docs/rules/no-empty
|
|
||||||
"no-ex-assign": 2, // http://eslint.org/docs/rules/no-ex-assign
|
|
||||||
"no-extra-boolean-cast": 0, // http://eslint.org/docs/rules/no-extra-boolean-cast
|
|
||||||
"no-extra-semi": 2, // http://eslint.org/docs/rules/no-extra-semi
|
|
||||||
"no-func-assign": 2, // http://eslint.org/docs/rules/no-func-assign
|
|
||||||
"no-inner-declarations": 2, // http://eslint.org/docs/rules/no-inner-declarations
|
|
||||||
"no-invalid-regexp": 2, // http://eslint.org/docs/rules/no-invalid-regexp
|
|
||||||
"no-irregular-whitespace": 2, // http://eslint.org/docs/rules/no-irregular-whitespace
|
|
||||||
"no-obj-calls": 2, // http://eslint.org/docs/rules/no-obj-calls
|
|
||||||
"no-sparse-arrays": 2, // http://eslint.org/docs/rules/no-sparse-arrays
|
|
||||||
"no-unreachable": 2, // http://eslint.org/docs/rules/no-unreachable
|
|
||||||
"use-isnan": 2, // http://eslint.org/docs/rules/use-isnan
|
|
||||||
"block-scoped-var": 2, // http://eslint.org/docs/rules/block-scoped-var
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Best practices
|
|
||||||
*/
|
|
||||||
"consistent-return": 2, // http://eslint.org/docs/rules/consistent-return
|
|
||||||
"curly": [2, "multi-line"], // http://eslint.org/docs/rules/curly
|
|
||||||
"default-case": 2, // http://eslint.org/docs/rules/default-case
|
|
||||||
"dot-notation": [2, { // http://eslint.org/docs/rules/dot-notation
|
|
||||||
"allowKeywords": true
|
|
||||||
}],
|
|
||||||
"eqeqeq": 2, // http://eslint.org/docs/rules/eqeqeq
|
|
||||||
"max-len": [2, 100, 4],
|
|
||||||
"guard-for-in": 2, // http://eslint.org/docs/rules/guard-for-in
|
|
||||||
"no-caller": 2, // http://eslint.org/docs/rules/no-caller
|
|
||||||
"no-else-return": 2, // http://eslint.org/docs/rules/no-else-return
|
|
||||||
"no-eq-null": 2, // http://eslint.org/docs/rules/no-eq-null
|
|
||||||
"no-eval": 2, // http://eslint.org/docs/rules/no-eval
|
|
||||||
"no-extend-native": 2, // http://eslint.org/docs/rules/no-extend-native
|
|
||||||
"no-extra-bind": 2, // http://eslint.org/docs/rules/no-extra-bind
|
|
||||||
"no-fallthrough": 2, // http://eslint.org/docs/rules/no-fallthrough
|
|
||||||
"no-floating-decimal": 2, // http://eslint.org/docs/rules/no-floating-decimal
|
|
||||||
"no-implied-eval": 2, // http://eslint.org/docs/rules/no-implied-eval
|
|
||||||
"no-lone-blocks": 2, // http://eslint.org/docs/rules/no-lone-blocks
|
|
||||||
"no-loop-func": 2, // http://eslint.org/docs/rules/no-loop-func
|
|
||||||
"no-multi-str": 2, // http://eslint.org/docs/rules/no-multi-str
|
|
||||||
"no-native-reassign": 2, // http://eslint.org/docs/rules/no-native-reassign
|
|
||||||
"no-new": 2, // http://eslint.org/docs/rules/no-new
|
|
||||||
"no-new-func": 2, // http://eslint.org/docs/rules/no-new-func
|
|
||||||
"no-new-wrappers": 2, // http://eslint.org/docs/rules/no-new-wrappers
|
|
||||||
"no-octal": 2, // http://eslint.org/docs/rules/no-octal
|
|
||||||
"no-octal-escape": 2, // http://eslint.org/docs/rules/no-octal-escape
|
|
||||||
"no-param-reassign": 2, // http://eslint.org/docs/rules/no-param-reassign
|
|
||||||
"no-proto": 2, // http://eslint.org/docs/rules/no-proto
|
|
||||||
"no-redeclare": 2, // http://eslint.org/docs/rules/no-redeclare
|
|
||||||
"no-return-assign": 2, // http://eslint.org/docs/rules/no-return-assign
|
|
||||||
"no-script-url": 2, // http://eslint.org/docs/rules/no-script-url
|
|
||||||
"no-self-compare": 2, // http://eslint.org/docs/rules/no-self-compare
|
|
||||||
"no-sequences": 2, // http://eslint.org/docs/rules/no-sequences
|
|
||||||
"no-throw-literal": 2, // http://eslint.org/docs/rules/no-throw-literal
|
|
||||||
"no-with": 2, // http://eslint.org/docs/rules/no-with
|
|
||||||
"radix": 2, // http://eslint.org/docs/rules/radix
|
|
||||||
"wrap-iife": [2, "any"], // http://eslint.org/docs/rules/wrap-iife
|
|
||||||
"yoda": 2, // http://eslint.org/docs/rules/yoda
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Style
|
|
||||||
*/
|
|
||||||
"indent": [2, "tab"], // http://eslint.org/docs/rules/indent
|
|
||||||
"quotes": [
|
|
||||||
2, "single", "avoid-escape" // http://eslint.org/docs/rules/quotes
|
|
||||||
],
|
|
||||||
"camelcase": [2, { // http://eslint.org/docs/rules/camelcase
|
|
||||||
"properties": "never"
|
|
||||||
}],
|
|
||||||
"comma-spacing": [2, { // http://eslint.org/docs/rules/comma-spacing
|
|
||||||
"before": false,
|
|
||||||
"after": true
|
|
||||||
}],
|
|
||||||
"comma-style": [2, "last"], // http://eslint.org/docs/rules/comma-style
|
|
||||||
"eol-last": 2, // http://eslint.org/docs/rules/eol-last
|
|
||||||
"func-names": 1, // http://eslint.org/docs/rules/func-names
|
|
||||||
"key-spacing": [2, { // http://eslint.org/docs/rules/key-spacing
|
|
||||||
"beforeColon": false,
|
|
||||||
"afterColon": true
|
|
||||||
}],
|
|
||||||
"no-multiple-empty-lines": [2, { // http://eslint.org/docs/rules/no-multiple-empty-lines
|
|
||||||
"max": 2
|
|
||||||
}],
|
|
||||||
"no-nested-ternary": 2, // http://eslint.org/docs/rules/no-nested-ternary
|
|
||||||
"no-new-object": 2, // http://eslint.org/docs/rules/no-new-object
|
|
||||||
"no-spaced-func": 2, // http://eslint.org/docs/rules/no-spaced-func
|
|
||||||
"no-trailing-spaces": 2, // http://eslint.org/docs/rules/no-trailing-spaces
|
|
||||||
"no-extra-parens": [2, "functions"], // http://eslint.org/docs/rules/no-extra-parens
|
|
||||||
"no-underscore-dangle": 0, // http://eslint.org/docs/rules/no-underscore-dangle
|
|
||||||
"padded-blocks": [2, "never"], // http://eslint.org/docs/rules/padded-blocks
|
|
||||||
"semi": [2, "always"], // http://eslint.org/docs/rules/semi
|
|
||||||
"semi-spacing": [2, { // http://eslint.org/docs/rules/semi-spacing
|
|
||||||
"before": false,
|
|
||||||
"after": true
|
|
||||||
}],
|
|
||||||
"space-after-keywords": 2, // http://eslint.org/docs/rules/space-after-keywords
|
|
||||||
"space-before-blocks": 2, // http://eslint.org/docs/rules/space-before-blocks
|
|
||||||
"space-before-function-paren": [2, "never"], // http://eslint.org/docs/rules/space-before-function-paren
|
|
||||||
"space-infix-ops": 2, // http://eslint.org/docs/rules/space-infix-ops
|
|
||||||
"space-return-throw-case": 2, // http://eslint.org/docs/rules/space-return-throw-case
|
|
||||||
"spaced-comment": [2, "always", {// http://eslint.org/docs/rules/spaced-comment
|
|
||||||
"exceptions": ["-", "+"],
|
|
||||||
"markers": ["=", "!"] // space here to support sprockets directives
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
elm-stuff
|
|
||||||
tests/test.js
|
|
||||||
node_modules/
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
sudo: false
|
|
||||||
|
|
||||||
cache:
|
|
||||||
directories:
|
|
||||||
- test/elm-stuff/build-artifacts
|
|
||||||
- sysconfcpus
|
|
||||||
|
|
||||||
language: node_js
|
|
||||||
|
|
||||||
node_js:
|
|
||||||
- "4.3"
|
|
||||||
|
|
||||||
before_install:
|
|
||||||
- if [ ${TRAVIS_OS_NAME} == "osx" ];
|
|
||||||
then brew update; brew install nvm; mkdir ~/.nvm; export NVM_DIR=~/.nvm; source $(brew --prefix nvm)/nvm.sh;
|
|
||||||
fi
|
|
||||||
- echo -e "Host github.com\n\tStrictHostKeyChecking no\n" >> ~/.ssh/config
|
|
||||||
- | # epic build time improvement - see https://github.com/elm-lang/elm-compiler/issues/1473#issuecomment-245704142
|
|
||||||
if [ ! -d sysconfcpus/bin ];
|
|
||||||
then
|
|
||||||
git clone https://github.com/obmarg/libsysconfcpus.git;
|
|
||||||
cd libsysconfcpus;
|
|
||||||
./configure --prefix=$TRAVIS_BUILD_DIR/sysconfcpus;
|
|
||||||
make && make install;
|
|
||||||
cd ..;
|
|
||||||
fi
|
|
||||||
|
|
||||||
install:
|
|
||||||
- npm install -g elm@0.18 elm-test
|
|
||||||
- mv $(npm config get prefix)/bin/elm-make $(npm config get prefix)/bin/elm-make-old
|
|
||||||
- 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
|
|
||||||
- chmod +x $(npm config get prefix)/bin/elm-make
|
|
||||||
|
|
||||||
script:
|
|
||||||
- bash tests/run-tests.sh
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
# Contributing to the core libraries
|
|
||||||
|
|
||||||
Thanks helping with the development of Elm! This document describes the basic
|
|
||||||
standards for opening pull requests and making the review process as smooth as
|
|
||||||
possible.
|
|
||||||
|
|
||||||
## Ground rules
|
|
||||||
|
|
||||||
* Always make pull requests minimal. If it can be split up, it should be split up.
|
|
||||||
* Use style consistent with the file you are modifying.
|
|
||||||
* Use descriptive titles for PRs
|
|
||||||
* Provide all the necessary context for evaluation in the PR.
|
|
||||||
If there are relevant issues or examples or discussions, add them.
|
|
||||||
If things can be summarized, summarize them. The easiest PRs are ones
|
|
||||||
that already address the reviewers questions and concerns.
|
|
||||||
|
|
||||||
## Documentation Fixes
|
|
||||||
|
|
||||||
If you want to fix docs, just open a PR. This is super helpful!
|
|
||||||
|
|
||||||
## Bug Fixes
|
|
||||||
|
|
||||||
If you find an issue or see one you want to work on, go for it!
|
|
||||||
|
|
||||||
The best strategy is often to dive in. Asking for directions usually
|
|
||||||
does not work. If someone knew the specifics and knew how how to fix
|
|
||||||
it, it is likely they would have already sent the PR themselves!
|
|
||||||
|
|
||||||
Also, be sure you are testing.
|
|
||||||
|
|
||||||
## Adding New Functions
|
|
||||||
|
|
||||||
We are fairly conservative about adding new functions to core libraries.
|
|
||||||
If you want to augment the `List` or `Array` library, we recommend creating
|
|
||||||
small packages called `list-extras` or `array-extras` that have all the
|
|
||||||
features you want. There are already several such packages maintained at
|
|
||||||
the [Elm Community organization](https://github.com/elm-community) that
|
|
||||||
welcome contributions in the form of pull requests.
|
|
||||||
|
|
||||||
Long term, we will set up a process to review `*-extras` packages to move
|
|
||||||
stuff into core. By going through packages, it will be much easier to assess
|
|
||||||
whether a function is pleasant and useful in practice before committing to it
|
|
||||||
in the core libraries.
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
Copyright (c) 2014-present, Evan Czaplicki
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Evan Czaplicki nor the names of other
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
# Elm Core Libraries
|
|
||||||
|
|
||||||
[](https://travis-ci.org/elm-lang/core)
|
|
||||||
|
|
||||||
Every Elm project needs the core libraries. They provide basic functionality including:
|
|
||||||
|
|
||||||
* The Basics — addition, subtraction, etc.
|
|
||||||
* Data Structures — lists, dictionaries, sets, etc.
|
|
||||||
|
|
||||||
|
|
||||||
## Default Imports
|
|
||||||
|
|
||||||
All Elm files have some default imports:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
import Basics exposing (..)
|
|
||||||
import List exposing ( List, (::) )
|
|
||||||
import Maybe exposing ( Maybe( Just, Nothing ) )
|
|
||||||
import Result exposing ( Result( Ok, Err ) )
|
|
||||||
import String
|
|
||||||
import Tuple
|
|
||||||
|
|
||||||
import Debug
|
|
||||||
|
|
||||||
import Platform exposing ( Program )
|
|
||||||
import Platform.Cmd exposing ( Cmd, (!) )
|
|
||||||
import Platform.Sub exposing ( Sub )
|
|
||||||
```
|
|
||||||
|
|
||||||
The intention is to include things that are both extremely useful and very
|
|
||||||
unlikely to overlap with anything that anyone will ever write in a library.
|
|
||||||
By keeping the set of default imports small, it also becomes easier to use
|
|
||||||
whatever version of `map` suits your fancy. Finally, it makes it easier to
|
|
||||||
figure out where the heck a function is coming from.
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
# 0.15
|
|
||||||
|
|
||||||
### Syntax
|
|
||||||
|
|
||||||
New `import` syntax with keyword `exposing`.
|
|
||||||
|
|
||||||
### Module Changes
|
|
||||||
|
|
||||||
* Move `Http` to `elm-http` package and totally redo API
|
|
||||||
* Remove `WebSocket` module
|
|
||||||
* Add `Task` module
|
|
||||||
|
|
||||||
### Channels become Mailboxes
|
|
||||||
|
|
||||||
`Graphics.Input` now works with this API (from module `Signal`):
|
|
||||||
|
|
||||||
```elm
|
|
||||||
type alias Mailbox a = { address : Address a, signal : Signal a }
|
|
||||||
|
|
||||||
mailbox : a -> Mailbox a
|
|
||||||
```
|
|
||||||
|
|
||||||
You can then send messages to the `Address` with functions like `Signal.send`
|
|
||||||
and `Signal.message`, or create forwarding addresses with `Signal.forwardTo`.
|
|
||||||
|
|
||||||
### Text in Collages
|
|
||||||
|
|
||||||
`Graphics.Collage` now has two new functions:
|
|
||||||
|
|
||||||
```elm
|
|
||||||
text : Text -> Form
|
|
||||||
outlinedText : LineStyle -> Text -> Form
|
|
||||||
```
|
|
||||||
|
|
||||||
These functions render text with the canvas, making things quite a bit faster.
|
|
||||||
The underlying implementation of `Text` has also been improved dramatically.
|
|
||||||
|
|
||||||
### Miscellaneous
|
|
||||||
|
|
||||||
* Change types of `head`, `tail`, `maximum`, `minimum` by wrapping output in `Maybe`
|
|
||||||
* Move `leftAligned`, `centered`, `rightAligned` from `Text` to `Graphics.Element`
|
|
||||||
* Move `asText` from `Text` to `Graphics.Element`, renaming it to `show` in the process
|
|
||||||
* Remove `Text.plainText` (can be replaced by `Graphics.Element.leftAligned << Text.fromString`)
|
|
||||||
* Change type of `Keyboard.keysDown` from `Signal (List KeyCode)` to `Signal (Set KeyCode)`
|
|
||||||
* Remove `Keyboard.directions`
|
|
||||||
* Rename `Keyboard.lastPressed` to `Keyboard.presses`
|
|
||||||
|
|
||||||
|
|
||||||
# 0.14
|
|
||||||
|
|
||||||
### Syntax
|
|
||||||
|
|
||||||
* Keyword `type` becomes `type alias`
|
|
||||||
* Keyword `data` becomes `type`
|
|
||||||
* Remove special list syntax in types, so `[a]` becomes `List a`
|
|
||||||
|
|
||||||
|
|
||||||
### Reduce Default Imports
|
|
||||||
|
|
||||||
The set of default imports has been reduced to the following:
|
|
||||||
|
|
||||||
```haskell
|
|
||||||
import Basics (..)
|
|
||||||
import Maybe ( Maybe( Just, Nothing ) )
|
|
||||||
import Result ( Result( Ok, Err ) )
|
|
||||||
import List ( List )
|
|
||||||
import Signal ( Signal )
|
|
||||||
```
|
|
||||||
|
|
||||||
### Make JSON parsing easy
|
|
||||||
|
|
||||||
* Added `Json.Decode` and `Json.Encode` libraries
|
|
||||||
|
|
||||||
|
|
||||||
### Use more natural names
|
|
||||||
|
|
||||||
* Rename `String.show` to `String.toString`
|
|
||||||
|
|
||||||
* Replace `List.zip` with `List.map2 (,)`
|
|
||||||
* Replace `List.zipWith f` with `List.map2 f`
|
|
||||||
|
|
||||||
* Rename `Signal.liftN` to `Signal.mapN`
|
|
||||||
* Rename `Signal.merges` to `Signal.mergeMany`
|
|
||||||
|
|
||||||
|
|
||||||
### Simplify Signal Library
|
|
||||||
|
|
||||||
* Revamp `Input` concept as `Signal.Channel`
|
|
||||||
* Remove `Signal.count`
|
|
||||||
* Remove `Signal.countIf`
|
|
||||||
* Remove `Signal.combine`
|
|
||||||
|
|
||||||
|
|
||||||
### Randomness Done Right
|
|
||||||
|
|
||||||
* No longer signal-based
|
|
||||||
* Use a `Generator` to create random values
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
### Revamp Maybes and Error Handling
|
|
||||||
|
|
||||||
* Add the following functions to `Maybe`
|
|
||||||
|
|
||||||
withDefault : a -> Maybe a -> a
|
|
||||||
oneOf : List (Maybe a) -> Maybe a
|
|
||||||
map : (a -> b) -> Maybe a -> Maybe b
|
|
||||||
andThen : Maybe a -> (a -> Maybe b) -> Maybe b
|
|
||||||
|
|
||||||
* Remove `Maybe.maybe` so `maybe 0 sqrt Nothing` becomes `withDefault 0 (map sqrt Nothing)`
|
|
||||||
|
|
||||||
* Remove `Maybe.isJust` and `Maybe.isNothing` in favor of pattern matching
|
|
||||||
|
|
||||||
* Add `Result` library for proper error handling. This is for cases when
|
|
||||||
you want a computation to succeed, but if there is a mistake, it should
|
|
||||||
produce a nice error message.
|
|
||||||
|
|
||||||
* Remove `Either` in favor of `Result` or custom union types
|
|
||||||
|
|
||||||
* Revamp functions that result in a `Maybe`.
|
|
||||||
|
|
||||||
- Remove `Dict.getOrElse` and `Dict.getOrFail` in favor of `withDefault 0 (Dict.get key dict)`
|
|
||||||
- Remove `Array.getOrElse` and `Array.getOrFail` in favor of `withDefault 0 (Array.get index array)`
|
|
||||||
- Change `String.toInt : String -> Maybe Int` to `String.toInt : String -> Result String Int`
|
|
||||||
- Change `String.toFloat : String -> Maybe Float` to `String.toFloat : String -> Result String Float`
|
|
||||||
|
|
||||||
|
|
||||||
### Make appending more logical
|
|
||||||
|
|
||||||
* Add the following functions to `Text`:
|
|
||||||
|
|
||||||
empty : Text
|
|
||||||
append : Text -> Text -> Text
|
|
||||||
concat : [Text] -> Text
|
|
||||||
join : Text -> [Text] -> Text
|
|
||||||
|
|
||||||
* Make the following changes in `List`:
|
|
||||||
- Replace `(++)` with `append`
|
|
||||||
- Remove `join`
|
|
||||||
|
|
||||||
### Miscellaneous
|
|
||||||
|
|
||||||
* Rename `Text.toText` to `Text.fromString`
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "5.1.1",
|
|
||||||
"summary": "Elm's standard libraries",
|
|
||||||
"repository": "http://github.com/elm-lang/core.git",
|
|
||||||
"license": "BSD3",
|
|
||||||
"source-directories": [
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"exposed-modules": [
|
|
||||||
"Array",
|
|
||||||
"Basics",
|
|
||||||
"Bitwise",
|
|
||||||
"Char",
|
|
||||||
"Color",
|
|
||||||
"Date",
|
|
||||||
"Debug",
|
|
||||||
"Dict",
|
|
||||||
"Json.Decode",
|
|
||||||
"Json.Encode",
|
|
||||||
"List",
|
|
||||||
"Maybe",
|
|
||||||
"Platform",
|
|
||||||
"Platform.Cmd",
|
|
||||||
"Platform.Sub",
|
|
||||||
"Process",
|
|
||||||
"Random",
|
|
||||||
"Regex",
|
|
||||||
"Result",
|
|
||||||
"Set",
|
|
||||||
"String",
|
|
||||||
"Task",
|
|
||||||
"Time",
|
|
||||||
"Tuple"
|
|
||||||
],
|
|
||||||
"native-modules": true,
|
|
||||||
"dependencies": {},
|
|
||||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
|
||||||
}
|
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
module Array exposing
|
|
||||||
( Array
|
|
||||||
, empty, repeat, initialize, fromList
|
|
||||||
, isEmpty, length, push, append
|
|
||||||
, get, set
|
|
||||||
, slice, toList, toIndexedList
|
|
||||||
, map, indexedMap, filter, foldl, foldr
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| A library for fast immutable arrays. The elements in an array must have the
|
|
||||||
same type. The arrays are implemented in Relaxed Radix Balanced-Trees for fast
|
|
||||||
reads, updates, and appends.
|
|
||||||
|
|
||||||
# Arrays
|
|
||||||
@docs Array
|
|
||||||
|
|
||||||
# Creating Arrays
|
|
||||||
@docs empty, repeat, initialize, fromList
|
|
||||||
|
|
||||||
# Basics
|
|
||||||
@docs isEmpty, length, push, append
|
|
||||||
|
|
||||||
# Get and Set
|
|
||||||
@docs get, set
|
|
||||||
|
|
||||||
# Taking Arrays Apart
|
|
||||||
@docs slice, toList, toIndexedList
|
|
||||||
|
|
||||||
# Mapping, Filtering, and Folding
|
|
||||||
@docs map, indexedMap, filter, foldl, foldr
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Array
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Maybe exposing (..)
|
|
||||||
import List
|
|
||||||
|
|
||||||
|
|
||||||
{-| Representation of fast immutable arrays. You can create arrays of integers
|
|
||||||
(`Array Int`) or strings (`Array String`) or any other type of value you can
|
|
||||||
dream up.
|
|
||||||
-}
|
|
||||||
type Array a = Array
|
|
||||||
|
|
||||||
|
|
||||||
{-| Initialize an array. `initialize n f` creates an array of length `n` with
|
|
||||||
the element at index `i` initialized to the result of `(f i)`.
|
|
||||||
|
|
||||||
initialize 4 identity == fromList [0,1,2,3]
|
|
||||||
initialize 4 (\n -> n*n) == fromList [0,1,4,9]
|
|
||||||
initialize 4 (always 0) == fromList [0,0,0,0]
|
|
||||||
-}
|
|
||||||
initialize : Int -> (Int -> a) -> Array a
|
|
||||||
initialize =
|
|
||||||
Native.Array.initialize
|
|
||||||
|
|
||||||
|
|
||||||
{-| Creates an array with a given length, filled with a default element.
|
|
||||||
|
|
||||||
repeat 5 0 == fromList [0,0,0,0,0]
|
|
||||||
repeat 3 "cat" == fromList ["cat","cat","cat"]
|
|
||||||
|
|
||||||
Notice that `repeat 3 x` is the same as `initialize 3 (always x)`.
|
|
||||||
-}
|
|
||||||
repeat : Int -> a -> Array a
|
|
||||||
repeat n e =
|
|
||||||
initialize n (always e)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create an array from a list.
|
|
||||||
-}
|
|
||||||
fromList : List a -> Array a
|
|
||||||
fromList =
|
|
||||||
Native.Array.fromList
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a list of elements from an array.
|
|
||||||
|
|
||||||
toList (fromList [3,5,8]) == [3,5,8]
|
|
||||||
-}
|
|
||||||
toList : Array a -> List a
|
|
||||||
toList =
|
|
||||||
Native.Array.toList
|
|
||||||
|
|
||||||
|
|
||||||
-- TODO: make this a native function.
|
|
||||||
{-| Create an indexed list from an array. Each element of the array will be
|
|
||||||
paired with its index.
|
|
||||||
|
|
||||||
toIndexedList (fromList ["cat","dog"]) == [(0,"cat"), (1,"dog")]
|
|
||||||
-}
|
|
||||||
toIndexedList : Array a -> List (Int, a)
|
|
||||||
toIndexedList array =
|
|
||||||
List.map2
|
|
||||||
(,)
|
|
||||||
(List.range 0 (Native.Array.length array - 1))
|
|
||||||
(Native.Array.toList array)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Apply a function on every element in an array.
|
|
||||||
|
|
||||||
map sqrt (fromList [1,4,9]) == fromList [1,2,3]
|
|
||||||
-}
|
|
||||||
map : (a -> b) -> Array a -> Array b
|
|
||||||
map =
|
|
||||||
Native.Array.map
|
|
||||||
|
|
||||||
|
|
||||||
{-| Apply a function on every element with its index as first argument.
|
|
||||||
|
|
||||||
indexedMap (*) (fromList [5,5,5]) == fromList [0,5,10]
|
|
||||||
-}
|
|
||||||
indexedMap : (Int -> a -> b) -> Array a -> Array b
|
|
||||||
indexedMap =
|
|
||||||
Native.Array.indexedMap
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reduce an array from the left. Read `foldl` as “fold from the left”.
|
|
||||||
|
|
||||||
foldl (::) [] (fromList [1,2,3]) == [3,2,1]
|
|
||||||
-}
|
|
||||||
foldl : (a -> b -> b) -> b -> Array a -> b
|
|
||||||
foldl =
|
|
||||||
Native.Array.foldl
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reduce an array from the right. Read `foldr` as “fold from the right”.
|
|
||||||
|
|
||||||
foldr (+) 0 (repeat 3 5) == 15
|
|
||||||
-}
|
|
||||||
foldr : (a -> b -> b) -> b -> Array a -> b
|
|
||||||
foldr =
|
|
||||||
Native.Array.foldr
|
|
||||||
|
|
||||||
|
|
||||||
{-| Keep only elements that satisfy the predicate:
|
|
||||||
|
|
||||||
filter isEven (fromList [1,2,3,4,5,6]) == (fromList [2,4,6])
|
|
||||||
-}
|
|
||||||
filter : (a -> Bool) -> Array a -> Array a
|
|
||||||
filter isOkay arr =
|
|
||||||
let
|
|
||||||
update x xs =
|
|
||||||
if isOkay x then
|
|
||||||
Native.Array.push x xs
|
|
||||||
else
|
|
||||||
xs
|
|
||||||
in
|
|
||||||
Native.Array.foldl update Native.Array.empty arr
|
|
||||||
|
|
||||||
{-| Return an empty array.
|
|
||||||
|
|
||||||
length empty == 0
|
|
||||||
-}
|
|
||||||
empty : Array a
|
|
||||||
empty =
|
|
||||||
Native.Array.empty
|
|
||||||
|
|
||||||
|
|
||||||
{-| Push an element to the end of an array.
|
|
||||||
|
|
||||||
push 3 (fromList [1,2]) == fromList [1,2,3]
|
|
||||||
-}
|
|
||||||
push : a -> Array a -> Array a
|
|
||||||
push =
|
|
||||||
Native.Array.push
|
|
||||||
|
|
||||||
|
|
||||||
{-| Return Just the element at the index or Nothing if the index is out of range.
|
|
||||||
|
|
||||||
get 0 (fromList [0,5,3]) == Just 0
|
|
||||||
get 2 (fromList [0,5,3]) == Just 3
|
|
||||||
get 5 (fromList [0,5,3]) == Nothing
|
|
||||||
get -1 (fromList [0,5,3]) == Nothing
|
|
||||||
|
|
||||||
-}
|
|
||||||
get : Int -> Array a -> Maybe a
|
|
||||||
get i array =
|
|
||||||
if 0 <= i && i < Native.Array.length array then
|
|
||||||
Just (Native.Array.get i array)
|
|
||||||
else
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Set the element at a particular index. Returns an updated array.
|
|
||||||
If the index is out of range, the array is unaltered.
|
|
||||||
|
|
||||||
set 1 7 (fromList [1,2,3]) == fromList [1,7,3]
|
|
||||||
-}
|
|
||||||
set : Int -> a -> Array a -> Array a
|
|
||||||
set =
|
|
||||||
Native.Array.set
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get a sub-section of an array: `(slice start end array)`. The `start` is a
|
|
||||||
zero-based index where we will start our slice. The `end` is a zero-based index
|
|
||||||
that indicates the end of the slice. The slice extracts up to but not including
|
|
||||||
`end`.
|
|
||||||
|
|
||||||
slice 0 3 (fromList [0,1,2,3,4]) == fromList [0,1,2]
|
|
||||||
slice 1 4 (fromList [0,1,2,3,4]) == fromList [1,2,3]
|
|
||||||
|
|
||||||
Both the `start` and `end` indexes can be negative, indicating an offset from
|
|
||||||
the end of the array.
|
|
||||||
|
|
||||||
slice 1 -1 (fromList [0,1,2,3,4]) == fromList [1,2,3]
|
|
||||||
slice -2 5 (fromList [0,1,2,3,4]) == fromList [3,4]
|
|
||||||
|
|
||||||
This makes it pretty easy to `pop` the last element off of an array: `slice 0 -1 array`
|
|
||||||
-}
|
|
||||||
slice : Int -> Int -> Array a -> Array a
|
|
||||||
slice =
|
|
||||||
Native.Array.slice
|
|
||||||
|
|
||||||
|
|
||||||
{-| Return the length of an array.
|
|
||||||
|
|
||||||
length (fromList [1,2,3]) == 3
|
|
||||||
-}
|
|
||||||
length : Array a -> Int
|
|
||||||
length =
|
|
||||||
Native.Array.length
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if an array is empty.
|
|
||||||
|
|
||||||
isEmpty empty == True
|
|
||||||
-}
|
|
||||||
isEmpty : Array a -> Bool
|
|
||||||
isEmpty array =
|
|
||||||
length array == 0
|
|
||||||
|
|
||||||
|
|
||||||
{-| Append two arrays to a new one.
|
|
||||||
|
|
||||||
append (repeat 2 42) (repeat 3 81) == fromList [42,42,81,81,81]
|
|
||||||
-}
|
|
||||||
append : Array a -> Array a -> Array a
|
|
||||||
append =
|
|
||||||
Native.Array.append
|
|
||||||
@@ -1,650 +0,0 @@
|
|||||||
module Basics exposing
|
|
||||||
( (==), (/=)
|
|
||||||
, (<), (>), (<=), (>=), max, min, Order (..), compare
|
|
||||||
, not, (&&), (||), xor
|
|
||||||
, (+), (-), (*), (/), (^), (//), rem, (%), negate, abs, sqrt, clamp, logBase, e
|
|
||||||
, pi, cos, sin, tan, acos, asin, atan, atan2
|
|
||||||
, round, floor, ceiling, truncate, toFloat
|
|
||||||
, degrees, radians, turns
|
|
||||||
, toPolar, fromPolar
|
|
||||||
, isNaN, isInfinite
|
|
||||||
, toString, (++)
|
|
||||||
, identity, always, (<|), (|>), (<<), (>>), flip, curry, uncurry, Never, never
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Tons of useful functions that get imported by default.
|
|
||||||
|
|
||||||
# Equality
|
|
||||||
@docs (==), (/=)
|
|
||||||
|
|
||||||
# Comparison
|
|
||||||
|
|
||||||
These functions only work on `comparable` types. This includes numbers,
|
|
||||||
characters, strings, lists of comparable things, and tuples of comparable
|
|
||||||
things. Note that tuples with 7 or more elements are not comparable; why
|
|
||||||
are your tuples so big?
|
|
||||||
|
|
||||||
@docs (<), (>), (<=), (>=), max, min, Order, compare
|
|
||||||
|
|
||||||
# Booleans
|
|
||||||
@docs not, (&&), (||), xor
|
|
||||||
|
|
||||||
# Mathematics
|
|
||||||
@docs (+), (-), (*), (/), (^), (//), rem, (%), negate, abs, sqrt, clamp, logBase, e
|
|
||||||
|
|
||||||
# Trigonometry
|
|
||||||
@docs pi, cos, sin, tan, acos, asin, atan, atan2
|
|
||||||
|
|
||||||
# Number Conversions
|
|
||||||
@docs round, floor, ceiling, truncate, toFloat
|
|
||||||
|
|
||||||
# Angle Conversions
|
|
||||||
All angle conversions result in “standard Elm angles”
|
|
||||||
which happen to be radians.
|
|
||||||
|
|
||||||
@docs degrees, radians, turns
|
|
||||||
|
|
||||||
# Polar Coordinates
|
|
||||||
@docs toPolar, fromPolar
|
|
||||||
|
|
||||||
# Floating Point Checks
|
|
||||||
@docs isNaN, isInfinite
|
|
||||||
|
|
||||||
# Strings and Lists
|
|
||||||
@docs toString, (++)
|
|
||||||
|
|
||||||
# Higher-Order Helpers
|
|
||||||
@docs identity, always, (<|), (|>), (<<), (>>), flip, curry, uncurry, Never, never
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Basics
|
|
||||||
import Native.Utils
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert radians to standard Elm angles (radians). -}
|
|
||||||
radians : Float -> Float
|
|
||||||
radians t =
|
|
||||||
t
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert degrees to standard Elm angles (radians). -}
|
|
||||||
degrees : Float -> Float
|
|
||||||
degrees =
|
|
||||||
Native.Basics.degrees
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert turns to standard Elm angles (radians).
|
|
||||||
One turn is equal to 360°.
|
|
||||||
-}
|
|
||||||
turns : Float -> Float
|
|
||||||
turns =
|
|
||||||
Native.Basics.turns
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert polar coordinates (r,θ) to Cartesian coordinates (x,y). -}
|
|
||||||
fromPolar : (Float,Float) -> (Float,Float)
|
|
||||||
fromPolar =
|
|
||||||
Native.Basics.fromPolar
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert Cartesian coordinates (x,y) to polar coordinates (r,θ). -}
|
|
||||||
toPolar : (Float,Float) -> (Float,Float)
|
|
||||||
toPolar =
|
|
||||||
Native.Basics.toPolar
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
(+) : number -> number -> number
|
|
||||||
(+) =
|
|
||||||
Native.Basics.add
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
(-) : number -> number -> number
|
|
||||||
(-) =
|
|
||||||
Native.Basics.sub
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
(*) : number -> number -> number
|
|
||||||
(*) =
|
|
||||||
Native.Basics.mul
|
|
||||||
|
|
||||||
|
|
||||||
{-| Floating point division. -}
|
|
||||||
(/) : Float -> Float -> Float
|
|
||||||
(/) =
|
|
||||||
Native.Basics.floatDiv
|
|
||||||
|
|
||||||
|
|
||||||
infixl 6 +
|
|
||||||
infixl 6 -
|
|
||||||
infixl 7 *
|
|
||||||
infixl 7 /
|
|
||||||
infixr 8 ^
|
|
||||||
|
|
||||||
infixl 7 //
|
|
||||||
infixl 7 %
|
|
||||||
|
|
||||||
|
|
||||||
{-| Integer division. The remainder is discarded. -}
|
|
||||||
(//) : Int -> Int -> Int
|
|
||||||
(//) =
|
|
||||||
Native.Basics.div
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the remainder after dividing one number by another.
|
|
||||||
|
|
||||||
rem 11 4 == 3
|
|
||||||
rem 12 4 == 0
|
|
||||||
rem 13 4 == 1
|
|
||||||
rem -1 4 == -1
|
|
||||||
-}
|
|
||||||
rem : Int -> Int -> Int
|
|
||||||
rem =
|
|
||||||
Native.Basics.rem
|
|
||||||
|
|
||||||
|
|
||||||
{-| Perform [modular arithmetic](http://en.wikipedia.org/wiki/Modular_arithmetic).
|
|
||||||
|
|
||||||
7 % 2 == 1
|
|
||||||
-1 % 4 == 3
|
|
||||||
-}
|
|
||||||
(%) : Int -> Int -> Int
|
|
||||||
(%) =
|
|
||||||
Native.Basics.mod
|
|
||||||
|
|
||||||
|
|
||||||
{-| Exponentiation
|
|
||||||
|
|
||||||
3^2 == 9
|
|
||||||
-}
|
|
||||||
(^) : number -> number -> number
|
|
||||||
(^) =
|
|
||||||
Native.Basics.exp
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
cos : Float -> Float
|
|
||||||
cos =
|
|
||||||
Native.Basics.cos
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
sin : Float -> Float
|
|
||||||
sin =
|
|
||||||
Native.Basics.sin
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
tan : Float -> Float
|
|
||||||
tan =
|
|
||||||
Native.Basics.tan
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
acos : Float -> Float
|
|
||||||
acos =
|
|
||||||
Native.Basics.acos
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
asin : Float -> Float
|
|
||||||
asin =
|
|
||||||
Native.Basics.asin
|
|
||||||
|
|
||||||
|
|
||||||
{-| You probably do not want to use this. It takes `(y/x)` as the
|
|
||||||
argument, so there is no way to know whether the negative signs comes from
|
|
||||||
the `y` or `x`. Thus, the resulting angle is always between π/2 and -π/2
|
|
||||||
(in quadrants I and IV). You probably want to use `atan2` instead.
|
|
||||||
-}
|
|
||||||
atan : Float -> Float
|
|
||||||
atan =
|
|
||||||
Native.Basics.atan
|
|
||||||
|
|
||||||
|
|
||||||
{-| This helps you find the angle of a Cartesian coordinate.
|
|
||||||
You will almost certainly want to use this instead of `atan`.
|
|
||||||
So `atan2 y x` computes *atan(y/x)* but also keeps track of which
|
|
||||||
quadrant the angle should really be in. The result will be between
|
|
||||||
π and -π, giving you the full range of angles.
|
|
||||||
-}
|
|
||||||
atan2 : Float -> Float -> Float
|
|
||||||
atan2 =
|
|
||||||
Native.Basics.atan2
|
|
||||||
|
|
||||||
|
|
||||||
{-| Take the square root of a number. -}
|
|
||||||
sqrt : Float -> Float
|
|
||||||
sqrt =
|
|
||||||
Native.Basics.sqrt
|
|
||||||
|
|
||||||
|
|
||||||
{-| Negate a number.
|
|
||||||
|
|
||||||
negate 42 == -42
|
|
||||||
negate -42 == 42
|
|
||||||
negate 0 == 0
|
|
||||||
-}
|
|
||||||
negate : number -> number
|
|
||||||
negate =
|
|
||||||
Native.Basics.negate
|
|
||||||
|
|
||||||
|
|
||||||
{-| Take the absolute value of a number. -}
|
|
||||||
abs : number -> number
|
|
||||||
abs =
|
|
||||||
Native.Basics.abs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Calculate the logarithm of a number with a given base.
|
|
||||||
|
|
||||||
logBase 10 100 == 2
|
|
||||||
logBase 2 256 == 8
|
|
||||||
-}
|
|
||||||
logBase : Float -> Float -> Float
|
|
||||||
logBase =
|
|
||||||
Native.Basics.logBase
|
|
||||||
|
|
||||||
|
|
||||||
{-| Clamps a number within a given range. With the expression
|
|
||||||
`clamp 100 200 x` the results are as follows:
|
|
||||||
|
|
||||||
100 if x < 100
|
|
||||||
x if 100 <= x < 200
|
|
||||||
200 if 200 <= x
|
|
||||||
-}
|
|
||||||
clamp : number -> number -> number -> number
|
|
||||||
clamp =
|
|
||||||
Native.Basics.clamp
|
|
||||||
|
|
||||||
|
|
||||||
{-| An approximation of pi. -}
|
|
||||||
pi : Float
|
|
||||||
pi =
|
|
||||||
Native.Basics.pi
|
|
||||||
|
|
||||||
|
|
||||||
{-| An approximation of e. -}
|
|
||||||
e : Float
|
|
||||||
e =
|
|
||||||
Native.Basics.e
|
|
||||||
|
|
||||||
|
|
||||||
{-| Check if values are “the same”.
|
|
||||||
|
|
||||||
**Note:** Elm uses structural equality on tuples, records, and user-defined
|
|
||||||
union types. This means the values `(3, 4)` and `(3, 4)` are definitely equal.
|
|
||||||
This is not true in languages like JavaScript that use reference equality on
|
|
||||||
objects.
|
|
||||||
|
|
||||||
**Note:** Equality (in the Elm sense) is not possible for certain types. For
|
|
||||||
example, the functions `(\n -> n + 1)` and `(\n -> 1 + n)` are “the
|
|
||||||
same” but detecting this in general is [undecidable][]. In a future
|
|
||||||
release, the compiler will detect when `(==)` is used with problematic
|
|
||||||
types and provide a helpful error message. This will require quite serious
|
|
||||||
infrastructure work that makes sense to batch with another big project, so the
|
|
||||||
stopgap is to crash as quickly as possible. Problematic types include functions
|
|
||||||
and JavaScript values like `Json.Encode.Value` which could contain functions
|
|
||||||
if passed through a port.
|
|
||||||
|
|
||||||
[undecidable]: https://en.wikipedia.org/wiki/Undecidable_problem
|
|
||||||
-}
|
|
||||||
(==) : a -> a -> Bool
|
|
||||||
(==) =
|
|
||||||
Native.Basics.eq
|
|
||||||
|
|
||||||
|
|
||||||
{-| Check if values are not “the same”.
|
|
||||||
|
|
||||||
So `(a /= b)` is the same as `(not (a == b))`.
|
|
||||||
-}
|
|
||||||
(/=) : a -> a -> Bool
|
|
||||||
(/=) =
|
|
||||||
Native.Basics.neq
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
(<) : comparable -> comparable -> Bool
|
|
||||||
(<) =
|
|
||||||
Native.Basics.lt
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
(>) : comparable -> comparable -> Bool
|
|
||||||
(>) =
|
|
||||||
Native.Basics.gt
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
(<=) : comparable -> comparable -> Bool
|
|
||||||
(<=) =
|
|
||||||
Native.Basics.le
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
(>=) : comparable -> comparable -> Bool
|
|
||||||
(>=) =
|
|
||||||
Native.Basics.ge
|
|
||||||
|
|
||||||
|
|
||||||
infix 4 ==
|
|
||||||
infix 4 /=
|
|
||||||
infix 4 <
|
|
||||||
infix 4 >
|
|
||||||
infix 4 <=
|
|
||||||
infix 4 >=
|
|
||||||
|
|
||||||
|
|
||||||
{-| Compare any two comparable values. Comparable values include `String`, `Char`,
|
|
||||||
`Int`, `Float`, `Time`, or a list or tuple containing comparable values.
|
|
||||||
These are also the only values that work as `Dict` keys or `Set` members.
|
|
||||||
-}
|
|
||||||
compare : comparable -> comparable -> Order
|
|
||||||
compare =
|
|
||||||
Native.Basics.compare
|
|
||||||
|
|
||||||
|
|
||||||
{-| Represents the relative ordering of two things.
|
|
||||||
The relations are less than, equal to, and greater than.
|
|
||||||
-}
|
|
||||||
type Order = LT | EQ | GT
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the smaller of two comparables. -}
|
|
||||||
min : comparable -> comparable -> comparable
|
|
||||||
min =
|
|
||||||
Native.Basics.min
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the larger of two comparables. -}
|
|
||||||
max : comparable -> comparable -> comparable
|
|
||||||
max =
|
|
||||||
Native.Basics.max
|
|
||||||
|
|
||||||
|
|
||||||
{-| The logical AND operator. `True` if both inputs are `True`.
|
|
||||||
|
|
||||||
**Note:** When used in the infix position, like `(left && right)`, the operator
|
|
||||||
short-circuits. This means if `left` is `False` we do not bother evaluating `right`
|
|
||||||
and just return `False` overall.
|
|
||||||
-}
|
|
||||||
(&&) : Bool -> Bool -> Bool
|
|
||||||
(&&) =
|
|
||||||
Native.Basics.and
|
|
||||||
|
|
||||||
|
|
||||||
{-| The logical OR operator. `True` if one or both inputs are `True`.
|
|
||||||
|
|
||||||
**Note:** When used in the infix position, like `(left || right)`, the operator
|
|
||||||
short-circuits. This means if `left` is `True` we do not bother evaluating `right`
|
|
||||||
and just return `True` overall.
|
|
||||||
-}
|
|
||||||
(||) : Bool -> Bool -> Bool
|
|
||||||
(||) =
|
|
||||||
Native.Basics.or
|
|
||||||
|
|
||||||
|
|
||||||
infixr 3 &&
|
|
||||||
infixr 2 ||
|
|
||||||
|
|
||||||
|
|
||||||
{-| The exclusive-or operator. `True` if exactly one input is `True`. -}
|
|
||||||
xor : Bool -> Bool -> Bool
|
|
||||||
xor =
|
|
||||||
Native.Basics.xor
|
|
||||||
|
|
||||||
|
|
||||||
{-| Negate a boolean value.
|
|
||||||
|
|
||||||
not True == False
|
|
||||||
not False == True
|
|
||||||
-}
|
|
||||||
not : Bool -> Bool
|
|
||||||
not =
|
|
||||||
Native.Basics.not
|
|
||||||
|
|
||||||
|
|
||||||
-- Conversions
|
|
||||||
|
|
||||||
{-| Round a number to the nearest integer. -}
|
|
||||||
round : Float -> Int
|
|
||||||
round =
|
|
||||||
Native.Basics.round
|
|
||||||
|
|
||||||
|
|
||||||
{-| Truncate a number, rounding towards zero. -}
|
|
||||||
truncate : Float -> Int
|
|
||||||
truncate =
|
|
||||||
Native.Basics.truncate
|
|
||||||
|
|
||||||
|
|
||||||
{-| Floor function, rounding down. -}
|
|
||||||
floor : Float -> Int
|
|
||||||
floor =
|
|
||||||
Native.Basics.floor
|
|
||||||
|
|
||||||
|
|
||||||
{-| Ceiling function, rounding up. -}
|
|
||||||
ceiling : Float -> Int
|
|
||||||
ceiling =
|
|
||||||
Native.Basics.ceiling
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert an integer into a float. -}
|
|
||||||
toFloat : Int -> Float
|
|
||||||
toFloat =
|
|
||||||
Native.Basics.toFloat
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine whether a float is an undefined or unrepresentable number.
|
|
||||||
NaN stands for *not a number* and it is [a standardized part of floating point
|
|
||||||
numbers](http://en.wikipedia.org/wiki/NaN).
|
|
||||||
|
|
||||||
isNaN (0/0) == True
|
|
||||||
isNaN (sqrt -1) == True
|
|
||||||
isNaN (1/0) == False -- infinity is a number
|
|
||||||
isNaN 1 == False
|
|
||||||
-}
|
|
||||||
isNaN : Float -> Bool
|
|
||||||
isNaN =
|
|
||||||
Native.Basics.isNaN
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine whether a float is positive or negative infinity.
|
|
||||||
|
|
||||||
isInfinite (0/0) == False
|
|
||||||
isInfinite (sqrt -1) == False
|
|
||||||
isInfinite (1/0) == True
|
|
||||||
isInfinite 1 == False
|
|
||||||
|
|
||||||
Notice that NaN is not infinite! For float `n` to be finite implies that
|
|
||||||
`not (isInfinite n || isNaN n)` evaluates to `True`.
|
|
||||||
-}
|
|
||||||
isInfinite : Float -> Bool
|
|
||||||
isInfinite =
|
|
||||||
Native.Basics.isInfinite
|
|
||||||
|
|
||||||
|
|
||||||
{-| Turn any kind of value into a string. When you view the resulting string
|
|
||||||
with `Text.fromString` it should look just like the value it came from.
|
|
||||||
|
|
||||||
toString 42 == "42"
|
|
||||||
toString [1,2] == "[1,2]"
|
|
||||||
toString "he said, \"hi\"" == "\"he said, \\\"hi\\\"\""
|
|
||||||
-}
|
|
||||||
toString : a -> String
|
|
||||||
toString =
|
|
||||||
Native.Utils.toString
|
|
||||||
|
|
||||||
|
|
||||||
{-| Put two appendable things together. This includes strings, lists, and text.
|
|
||||||
|
|
||||||
"hello" ++ "world" == "helloworld"
|
|
||||||
[1,1,2] ++ [3,5,8] == [1,1,2,3,5,8]
|
|
||||||
-}
|
|
||||||
(++) : appendable -> appendable -> appendable
|
|
||||||
(++) =
|
|
||||||
Native.Utils.append
|
|
||||||
|
|
||||||
|
|
||||||
infixr 5 ++
|
|
||||||
|
|
||||||
|
|
||||||
-- Function Helpers
|
|
||||||
|
|
||||||
{-| Function composition, passing results along in the suggested direction. For
|
|
||||||
example, the following code checks if the square root of a number is odd:
|
|
||||||
|
|
||||||
not << isEven << sqrt
|
|
||||||
|
|
||||||
You can think of this operator as equivalent to the following:
|
|
||||||
|
|
||||||
(g << f) == (\x -> g (f x))
|
|
||||||
|
|
||||||
So our example expands out to something like this:
|
|
||||||
|
|
||||||
\n -> not (isEven (sqrt n))
|
|
||||||
-}
|
|
||||||
(<<) : (b -> c) -> (a -> b) -> (a -> c)
|
|
||||||
(<<) g f x =
|
|
||||||
g (f x)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Function composition, passing results along in the suggested direction. For
|
|
||||||
example, the following code checks if the square root of a number is odd:
|
|
||||||
|
|
||||||
sqrt >> isEven >> not
|
|
||||||
|
|
||||||
This direction of function composition seems less pleasant than `(<<)` which
|
|
||||||
reads nicely in expressions like: `filter (not << isRegistered) students`
|
|
||||||
-}
|
|
||||||
(>>) : (a -> b) -> (b -> c) -> (a -> c)
|
|
||||||
(>>) f g x =
|
|
||||||
g (f x)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Forward function application `x |> f == f x`. This function is useful
|
|
||||||
for avoiding parentheses and writing code in a more natural way.
|
|
||||||
Consider the following code to create a pentagon:
|
|
||||||
|
|
||||||
scale 2 (move (10,10) (filled blue (ngon 5 30)))
|
|
||||||
|
|
||||||
This can also be written as:
|
|
||||||
|
|
||||||
ngon 5 30
|
|
||||||
|> filled blue
|
|
||||||
|> move (10,10)
|
|
||||||
|> scale 2
|
|
||||||
-}
|
|
||||||
(|>) : a -> (a -> b) -> b
|
|
||||||
(|>) x f =
|
|
||||||
f x
|
|
||||||
|
|
||||||
|
|
||||||
{-| Backward function application `f <| x == f x`. This function is useful for
|
|
||||||
avoiding parentheses. Consider the following code to create a text element:
|
|
||||||
|
|
||||||
leftAligned (monospace (fromString "code"))
|
|
||||||
|
|
||||||
This can also be written as:
|
|
||||||
|
|
||||||
leftAligned <| monospace <| fromString "code"
|
|
||||||
-}
|
|
||||||
(<|) : (a -> b) -> a -> b
|
|
||||||
(<|) f x =
|
|
||||||
f x
|
|
||||||
|
|
||||||
|
|
||||||
infixr 9 <<
|
|
||||||
infixl 9 >>
|
|
||||||
infixr 0 <|
|
|
||||||
infixl 0 |>
|
|
||||||
|
|
||||||
|
|
||||||
{-| Given a value, returns exactly the same value. This is called
|
|
||||||
[the identity function](http://en.wikipedia.org/wiki/Identity_function).
|
|
||||||
-}
|
|
||||||
identity : a -> a
|
|
||||||
identity x =
|
|
||||||
x
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a function that *always* returns the same value. Useful with
|
|
||||||
functions like `map`:
|
|
||||||
|
|
||||||
List.map (always 0) [1,2,3,4,5] == [0,0,0,0,0]
|
|
||||||
|
|
||||||
-- List.map (\_ -> 0) [1,2,3,4,5] == [0,0,0,0,0]
|
|
||||||
-- always = (\x _ -> x)
|
|
||||||
-}
|
|
||||||
always : a -> b -> a
|
|
||||||
always a _ =
|
|
||||||
a
|
|
||||||
|
|
||||||
|
|
||||||
{-| Flip the order of the first two arguments to a function. -}
|
|
||||||
flip : (a -> b -> c) -> (b -> a -> c)
|
|
||||||
flip f b a =
|
|
||||||
f a b
|
|
||||||
|
|
||||||
|
|
||||||
{-| Change how arguments are passed to a function.
|
|
||||||
This splits paired arguments into two separate arguments.
|
|
||||||
-}
|
|
||||||
curry : ((a,b) -> c) -> a -> b -> c
|
|
||||||
curry f a b =
|
|
||||||
f (a,b)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Change how arguments are passed to a function.
|
|
||||||
This combines two arguments into a single pair.
|
|
||||||
-}
|
|
||||||
uncurry : (a -> b -> c) -> (a,b) -> c
|
|
||||||
uncurry f (a,b) =
|
|
||||||
f a b
|
|
||||||
|
|
||||||
|
|
||||||
{-| A value that can never happen! For context:
|
|
||||||
|
|
||||||
- The boolean type `Bool` has two values: `True` and `False`
|
|
||||||
- The unit type `()` has one value: `()`
|
|
||||||
- The never type `Never` has no values!
|
|
||||||
|
|
||||||
You may see it in the wild in `Html Never` which means this HTML will never
|
|
||||||
produce any messages. You would need to write an event handler like
|
|
||||||
`onClick ??? : Attribute Never` but how can we fill in the question marks?!
|
|
||||||
So there cannot be any event handlers on that HTML.
|
|
||||||
|
|
||||||
You may also see this used with tasks that never fail, like `Task Never ()`.
|
|
||||||
|
|
||||||
The `Never` type is useful for restricting *arguments* to a function. Maybe my
|
|
||||||
API can only accept HTML without event handlers, so I require `Html Never` and
|
|
||||||
users can give `Html msg` and everything will go fine. Generally speaking, you
|
|
||||||
do not want `Never` in your return types though.
|
|
||||||
-}
|
|
||||||
type Never = JustOneMore Never
|
|
||||||
|
|
||||||
|
|
||||||
{-| A function that can never be called. Seems extremely pointless, but it
|
|
||||||
*can* come in handy. Imagine you have some HTML that should never produce any
|
|
||||||
messages. And say you want to use it in some other HTML that *does* produce
|
|
||||||
messages. You could say:
|
|
||||||
|
|
||||||
import Html exposing (..)
|
|
||||||
|
|
||||||
embedHtml : Html Never -> Html msg
|
|
||||||
embedHtml staticStuff =
|
|
||||||
div []
|
|
||||||
[ text "hello"
|
|
||||||
, Html.map never staticStuff
|
|
||||||
]
|
|
||||||
|
|
||||||
So the `never` function is basically telling the type system, make sure no one
|
|
||||||
ever calls me!
|
|
||||||
-}
|
|
||||||
never : Never -> a
|
|
||||||
never (JustOneMore nvr) =
|
|
||||||
never nvr
|
|
||||||
@@ -1,90 +0,0 @@
|
|||||||
module Bitwise exposing
|
|
||||||
( and, or, xor, complement
|
|
||||||
, shiftLeftBy, shiftRightBy, shiftRightZfBy
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Library for [bitwise operations](http://en.wikipedia.org/wiki/Bitwise_operation).
|
|
||||||
|
|
||||||
# Basic Operations
|
|
||||||
@docs and, or, xor, complement
|
|
||||||
|
|
||||||
# Bit Shifts
|
|
||||||
@docs shiftLeftBy, shiftRightBy, shiftRightZfBy
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Bitwise
|
|
||||||
|
|
||||||
|
|
||||||
{-| Bitwise AND
|
|
||||||
-}
|
|
||||||
and : Int -> Int -> Int
|
|
||||||
and =
|
|
||||||
Native.Bitwise.and
|
|
||||||
|
|
||||||
|
|
||||||
{-| Bitwise OR
|
|
||||||
-}
|
|
||||||
or : Int -> Int -> Int
|
|
||||||
or =
|
|
||||||
Native.Bitwise.or
|
|
||||||
|
|
||||||
|
|
||||||
{-| Bitwise XOR
|
|
||||||
-}
|
|
||||||
xor : Int -> Int -> Int
|
|
||||||
xor =
|
|
||||||
Native.Bitwise.xor
|
|
||||||
|
|
||||||
|
|
||||||
{-| Flip each bit individually, often called bitwise NOT
|
|
||||||
-}
|
|
||||||
complement : Int -> Int
|
|
||||||
complement =
|
|
||||||
Native.Bitwise.complement
|
|
||||||
|
|
||||||
|
|
||||||
{-| Shift bits to the left by a given offset, filling new bits with zeros.
|
|
||||||
This can be used to multiply numbers by powers of two.
|
|
||||||
|
|
||||||
shiftLeftBy 1 5 == 10
|
|
||||||
shiftLeftBy 5 1 == 32
|
|
||||||
-}
|
|
||||||
shiftLeftBy : Int -> Int -> Int
|
|
||||||
shiftLeftBy =
|
|
||||||
Native.Bitwise.shiftLeftBy
|
|
||||||
|
|
||||||
|
|
||||||
{-| Shift bits to the right by a given offset, filling new bits with
|
|
||||||
whatever is the topmost bit. This can be used to divide numbers by powers of two.
|
|
||||||
|
|
||||||
shiftRightBy 1 32 == 16
|
|
||||||
shiftRightBy 2 32 == 8
|
|
||||||
shiftRightBy 1 -32 == -16
|
|
||||||
|
|
||||||
This is called an [arithmetic right shift][ars], often written (>>), and
|
|
||||||
sometimes called a sign-propagating right shift because it fills empty spots
|
|
||||||
with copies of the highest bit.
|
|
||||||
|
|
||||||
[ars]: http://en.wikipedia.org/wiki/Bitwise_operation#Arithmetic_shift
|
|
||||||
-}
|
|
||||||
shiftRightBy : Int -> Int -> Int
|
|
||||||
shiftRightBy =
|
|
||||||
Native.Bitwise.shiftRightBy
|
|
||||||
|
|
||||||
|
|
||||||
{-| Shift bits to the right by a given offset, filling new bits with zeros.
|
|
||||||
|
|
||||||
shiftRightZfBy 1 32 == 16
|
|
||||||
shiftRightZfBy 2 32 == 8
|
|
||||||
shiftRightZfBy 1 -32 == 2147483632
|
|
||||||
|
|
||||||
This is called an [logical right shift][lrs], often written (>>>), and
|
|
||||||
sometimes called a zero-fill right shift because it fills empty spots with
|
|
||||||
zeros.
|
|
||||||
|
|
||||||
[lrs]: http://en.wikipedia.org/wiki/Bitwise_operation#Logical_shift
|
|
||||||
-}
|
|
||||||
shiftRightZfBy : Int -> Int -> Int
|
|
||||||
shiftRightZfBy =
|
|
||||||
Native.Bitwise.shiftRightZfBy
|
|
||||||
|
|
||||||
@@ -1,103 +0,0 @@
|
|||||||
module Char exposing
|
|
||||||
( isUpper, isLower, isDigit, isOctDigit, isHexDigit
|
|
||||||
, toUpper, toLower, toLocaleUpper, toLocaleLower
|
|
||||||
, KeyCode, toCode, fromCode
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Functions for working with characters. Character literals are enclosed in
|
|
||||||
`'a'` pair of single quotes.
|
|
||||||
|
|
||||||
# Classification
|
|
||||||
@docs isUpper, isLower, isDigit, isOctDigit, isHexDigit
|
|
||||||
|
|
||||||
# Conversion
|
|
||||||
@docs toUpper, toLower, toLocaleUpper, toLocaleLower
|
|
||||||
|
|
||||||
# Key Codes
|
|
||||||
@docs KeyCode, toCode, fromCode
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Char
|
|
||||||
import Basics exposing ((&&), (||), (>=), (<=))
|
|
||||||
|
|
||||||
|
|
||||||
isBetween : Char -> Char -> Char -> Bool
|
|
||||||
isBetween low high char =
|
|
||||||
let code = toCode char
|
|
||||||
in
|
|
||||||
(code >= toCode low) && (code <= toCode high)
|
|
||||||
|
|
||||||
|
|
||||||
{-| True for upper case ASCII letters. -}
|
|
||||||
isUpper : Char -> Bool
|
|
||||||
isUpper =
|
|
||||||
isBetween 'A' 'Z'
|
|
||||||
|
|
||||||
|
|
||||||
{-| True for lower case ASCII letters. -}
|
|
||||||
isLower : Char -> Bool
|
|
||||||
isLower =
|
|
||||||
isBetween 'a' 'z'
|
|
||||||
|
|
||||||
|
|
||||||
{-| True for ASCII digits `[0-9]`. -}
|
|
||||||
isDigit : Char -> Bool
|
|
||||||
isDigit =
|
|
||||||
isBetween '0' '9'
|
|
||||||
|
|
||||||
|
|
||||||
{-| True for ASCII octal digits `[0-7]`. -}
|
|
||||||
isOctDigit : Char -> Bool
|
|
||||||
isOctDigit =
|
|
||||||
isBetween '0' '7'
|
|
||||||
|
|
||||||
|
|
||||||
{-| True for ASCII hexadecimal digits `[0-9a-fA-F]`. -}
|
|
||||||
isHexDigit : Char -> Bool
|
|
||||||
isHexDigit char =
|
|
||||||
isDigit char || isBetween 'a' 'f' char || isBetween 'A' 'F' char
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert to upper case. -}
|
|
||||||
toUpper : Char -> Char
|
|
||||||
toUpper =
|
|
||||||
Native.Char.toUpper
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert to lower case. -}
|
|
||||||
toLower : Char -> Char
|
|
||||||
toLower =
|
|
||||||
Native.Char.toLower
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert to upper case, according to any locale-specific case mappings. -}
|
|
||||||
toLocaleUpper : Char -> Char
|
|
||||||
toLocaleUpper =
|
|
||||||
Native.Char.toLocaleUpper
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert to lower case, according to any locale-specific case mappings. -}
|
|
||||||
toLocaleLower : Char -> Char
|
|
||||||
toLocaleLower =
|
|
||||||
Native.Char.toLocaleLower
|
|
||||||
|
|
||||||
|
|
||||||
{-| Keyboard keys can be represented as integers. These are called *key codes*.
|
|
||||||
You can use [`toCode`](#toCode) and [`fromCode`](#fromCode) to convert between
|
|
||||||
key codes and characters.
|
|
||||||
-}
|
|
||||||
type alias KeyCode = Int
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert to key code.
|
|
||||||
-}
|
|
||||||
toCode : Char -> KeyCode
|
|
||||||
toCode =
|
|
||||||
Native.Char.toCode
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert from key code. -}
|
|
||||||
fromCode : KeyCode -> Char
|
|
||||||
fromCode =
|
|
||||||
Native.Char.fromCode
|
|
||||||
@@ -1,456 +0,0 @@
|
|||||||
module Color exposing
|
|
||||||
( Color, rgb, rgba, hsl, hsla, greyscale, grayscale, complement
|
|
||||||
, Gradient, linear, radial
|
|
||||||
, toRgb, toHsl
|
|
||||||
, red, orange, yellow, green, blue, purple, brown
|
|
||||||
, lightRed, lightOrange, lightYellow, lightGreen, lightBlue, lightPurple, lightBrown
|
|
||||||
, darkRed, darkOrange, darkYellow, darkGreen, darkBlue, darkPurple, darkBrown
|
|
||||||
, white, lightGrey, grey, darkGrey, lightCharcoal, charcoal, darkCharcoal, black
|
|
||||||
, lightGray, gray, darkGray
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Library for working with colors. Includes
|
|
||||||
[RGB](https://en.wikipedia.org/wiki/RGB_color_model) and
|
|
||||||
[HSL](http://en.wikipedia.org/wiki/HSL_and_HSV) creation, gradients, and
|
|
||||||
built-in names.
|
|
||||||
|
|
||||||
# Colors
|
|
||||||
@docs Color
|
|
||||||
|
|
||||||
# Creation
|
|
||||||
@docs rgb, rgba, hsl, hsla, greyscale, grayscale, complement
|
|
||||||
|
|
||||||
# Gradients
|
|
||||||
@docs Gradient, linear, radial
|
|
||||||
|
|
||||||
# Extracting Colors
|
|
||||||
@docs toRgb, toHsl
|
|
||||||
|
|
||||||
# Built-in Colors
|
|
||||||
These colors come from the [Tango
|
|
||||||
palette](http://tango.freedesktop.org/Tango_Icon_Theme_Guidelines)
|
|
||||||
which provides aesthetically reasonable defaults for colors. Each color also
|
|
||||||
comes with a light and dark version.
|
|
||||||
|
|
||||||
### Standard
|
|
||||||
@docs red, orange, yellow, green, blue, purple, brown
|
|
||||||
|
|
||||||
### Light
|
|
||||||
@docs lightRed, lightOrange, lightYellow, lightGreen, lightBlue, lightPurple, lightBrown
|
|
||||||
|
|
||||||
### Dark
|
|
||||||
@docs darkRed, darkOrange, darkYellow, darkGreen, darkBlue, darkPurple, darkBrown
|
|
||||||
|
|
||||||
### Eight Shades of Grey
|
|
||||||
These colors are a compatible series of shades of grey, fitting nicely
|
|
||||||
with the Tango palette.
|
|
||||||
@docs white, lightGrey, grey, darkGrey, lightCharcoal, charcoal, darkCharcoal, black
|
|
||||||
|
|
||||||
These are identical to the *grey* versions. It seems the spelling is regional, but
|
|
||||||
that has never helped me remember which one I should be writing.
|
|
||||||
@docs lightGray, gray, darkGray
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Representation of colors.
|
|
||||||
-}
|
|
||||||
type Color
|
|
||||||
= RGBA Int Int Int Float
|
|
||||||
| HSLA Float Float Float Float
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create RGB colors with an alpha component for transparency.
|
|
||||||
The alpha component is specified with numbers between 0 and 1. -}
|
|
||||||
rgba : Int -> Int -> Int -> Float -> Color
|
|
||||||
rgba =
|
|
||||||
RGBA
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create RGB colors from numbers between 0 and 255 inclusive. -}
|
|
||||||
rgb : Int -> Int -> Int -> Color
|
|
||||||
rgb r g b =
|
|
||||||
RGBA r g b 1
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create [HSL colors](http://en.wikipedia.org/wiki/HSL_and_HSV)
|
|
||||||
with an alpha component for transparency.
|
|
||||||
-}
|
|
||||||
hsla : Float -> Float -> Float -> Float -> Color
|
|
||||||
hsla hue saturation lightness alpha =
|
|
||||||
HSLA (hue - turns (toFloat (floor (hue / (2*pi))))) saturation lightness alpha
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create [HSL colors](http://en.wikipedia.org/wiki/HSL_and_HSV). This gives
|
|
||||||
you access to colors more like a color wheel, where all hues are arranged in a
|
|
||||||
circle that you specify with standard Elm angles (radians).
|
|
||||||
|
|
||||||
red = hsl (degrees 0) 1 0.5
|
|
||||||
green = hsl (degrees 120) 1 0.5
|
|
||||||
blue = hsl (degrees 240) 1 0.5
|
|
||||||
|
|
||||||
pastelRed = hsl (degrees 0) 0.7 0.7
|
|
||||||
|
|
||||||
To cycle through all colors, just cycle through degrees. The saturation level
|
|
||||||
is how vibrant the color is, like a dial between grey and bright colors. The
|
|
||||||
lightness level is a dial between white and black.
|
|
||||||
-}
|
|
||||||
hsl : Float -> Float -> Float -> Color
|
|
||||||
hsl hue saturation lightness =
|
|
||||||
hsla hue saturation lightness 1
|
|
||||||
|
|
||||||
|
|
||||||
{-| Produce a gray based on the input. 0 is white, 1 is black.
|
|
||||||
-}
|
|
||||||
grayscale : Float -> Color
|
|
||||||
grayscale p =
|
|
||||||
HSLA 0 0 (1-p) 1
|
|
||||||
|
|
||||||
|
|
||||||
{-| Produce a gray based on the input. 0 is white, 1 is black.
|
|
||||||
-}
|
|
||||||
greyscale : Float -> Color
|
|
||||||
greyscale p =
|
|
||||||
HSLA 0 0 (1-p) 1
|
|
||||||
|
|
||||||
|
|
||||||
{-| Produce a “complementary color”. The two colors will
|
|
||||||
accent each other. This is the same as rotating the hue by 180°.
|
|
||||||
-}
|
|
||||||
complement : Color -> Color
|
|
||||||
complement color =
|
|
||||||
case color of
|
|
||||||
HSLA h s l a ->
|
|
||||||
hsla (h + degrees 180) s l a
|
|
||||||
|
|
||||||
RGBA r g b a ->
|
|
||||||
let
|
|
||||||
(h,s,l) = rgbToHsl r g b
|
|
||||||
in
|
|
||||||
hsla (h + degrees 180) s l a
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the components of a color in the HSL format.
|
|
||||||
-}
|
|
||||||
toHsl : Color -> { hue:Float, saturation:Float, lightness:Float, alpha:Float }
|
|
||||||
toHsl color =
|
|
||||||
case color of
|
|
||||||
HSLA h s l a ->
|
|
||||||
{ hue=h, saturation=s, lightness=l, alpha=a }
|
|
||||||
|
|
||||||
RGBA r g b a ->
|
|
||||||
let
|
|
||||||
(h,s,l) = rgbToHsl r g b
|
|
||||||
in
|
|
||||||
{ hue=h, saturation=s, lightness=l, alpha=a }
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the components of a color in the RGB format.
|
|
||||||
-}
|
|
||||||
toRgb : Color -> { red:Int, green:Int, blue:Int, alpha:Float }
|
|
||||||
toRgb color =
|
|
||||||
case color of
|
|
||||||
RGBA r g b a ->
|
|
||||||
{ red = r, green = g, blue = b, alpha = a }
|
|
||||||
|
|
||||||
HSLA h s l a ->
|
|
||||||
let
|
|
||||||
(r,g,b) = hslToRgb h s l
|
|
||||||
in
|
|
||||||
{ red = round (255 * r)
|
|
||||||
, green = round (255 * g)
|
|
||||||
, blue = round (255 * b)
|
|
||||||
, alpha = a
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
fmod : Float -> Int -> Float
|
|
||||||
fmod f n =
|
|
||||||
let
|
|
||||||
integer = floor f
|
|
||||||
in
|
|
||||||
toFloat (integer % n) + f - toFloat integer
|
|
||||||
|
|
||||||
|
|
||||||
rgbToHsl : Int -> Int -> Int -> (Float,Float,Float)
|
|
||||||
rgbToHsl red green blue =
|
|
||||||
let
|
|
||||||
r = toFloat red / 255
|
|
||||||
g = toFloat green / 255
|
|
||||||
b = toFloat blue / 255
|
|
||||||
|
|
||||||
cMax = max (max r g) b
|
|
||||||
cMin = min (min r g) b
|
|
||||||
|
|
||||||
c = cMax - cMin
|
|
||||||
|
|
||||||
hue =
|
|
||||||
degrees 60 *
|
|
||||||
if cMax == r then
|
|
||||||
fmod ((g - b) / c) 6
|
|
||||||
else if cMax == g then
|
|
||||||
((b - r) / c) + 2
|
|
||||||
else {- cMax == b -}
|
|
||||||
((r - g) / c) + 4
|
|
||||||
|
|
||||||
lightness =
|
|
||||||
(cMax + cMin) / 2
|
|
||||||
|
|
||||||
saturation =
|
|
||||||
if lightness == 0 then
|
|
||||||
0
|
|
||||||
else
|
|
||||||
c / (1 - abs (2 * lightness - 1))
|
|
||||||
in
|
|
||||||
(hue, saturation, lightness)
|
|
||||||
|
|
||||||
|
|
||||||
hslToRgb : Float -> Float -> Float -> (Float,Float,Float)
|
|
||||||
hslToRgb hue saturation lightness =
|
|
||||||
let
|
|
||||||
chroma = (1 - abs (2 * lightness - 1)) * saturation
|
|
||||||
normHue = hue / degrees 60
|
|
||||||
|
|
||||||
x = chroma * (1 - abs (fmod normHue 2 - 1))
|
|
||||||
|
|
||||||
(r,g,b) =
|
|
||||||
if normHue < 0 then (0, 0, 0)
|
|
||||||
else if normHue < 1 then (chroma, x, 0)
|
|
||||||
else if normHue < 2 then (x, chroma, 0)
|
|
||||||
else if normHue < 3 then (0, chroma, x)
|
|
||||||
else if normHue < 4 then (0, x, chroma)
|
|
||||||
else if normHue < 5 then (x, 0, chroma)
|
|
||||||
else if normHue < 6 then (chroma, 0, x)
|
|
||||||
else (0, 0, 0)
|
|
||||||
|
|
||||||
m = lightness - chroma / 2
|
|
||||||
in
|
|
||||||
(r + m, g + m, b + m)
|
|
||||||
|
|
||||||
|
|
||||||
--toV3 : Color -> V3
|
|
||||||
|
|
||||||
--toV4 : Color -> V4
|
|
||||||
|
|
||||||
{-| Abstract representation of a color gradient.
|
|
||||||
-}
|
|
||||||
type Gradient
|
|
||||||
= Linear (Float,Float) (Float,Float) (List (Float,Color))
|
|
||||||
| Radial (Float,Float) Float (Float,Float) Float (List (Float,Color))
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a linear gradient. Takes a start and end point and then a series of
|
|
||||||
“color stops” that indicate how to interpolate between the start and
|
|
||||||
end points. See [this example](http://elm-lang.org/examples/linear-gradient) for a
|
|
||||||
more visual explanation.
|
|
||||||
-}
|
|
||||||
linear : (Float, Float) -> (Float, Float) -> List (Float,Color) -> Gradient
|
|
||||||
linear =
|
|
||||||
Linear
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a radial gradient. First takes a start point and inner radius. Then
|
|
||||||
takes an end point and outer radius. It then takes a series of “color
|
|
||||||
stops” that indicate how to interpolate between the inner and outer
|
|
||||||
circles. See [this example](http://elm-lang.org/examples/radial-gradient) for a
|
|
||||||
more visual explanation.
|
|
||||||
-}
|
|
||||||
radial : (Float,Float) -> Float -> (Float,Float) -> Float -> List (Float,Color) -> Gradient
|
|
||||||
radial =
|
|
||||||
Radial
|
|
||||||
|
|
||||||
|
|
||||||
-- BUILT-IN COLORS
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightRed : Color
|
|
||||||
lightRed =
|
|
||||||
RGBA 239 41 41 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
red : Color
|
|
||||||
red =
|
|
||||||
RGBA 204 0 0 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkRed : Color
|
|
||||||
darkRed =
|
|
||||||
RGBA 164 0 0 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightOrange : Color
|
|
||||||
lightOrange =
|
|
||||||
RGBA 252 175 62 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
orange : Color
|
|
||||||
orange =
|
|
||||||
RGBA 245 121 0 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkOrange : Color
|
|
||||||
darkOrange =
|
|
||||||
RGBA 206 92 0 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightYellow : Color
|
|
||||||
lightYellow =
|
|
||||||
RGBA 255 233 79 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
yellow : Color
|
|
||||||
yellow =
|
|
||||||
RGBA 237 212 0 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkYellow : Color
|
|
||||||
darkYellow =
|
|
||||||
RGBA 196 160 0 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightGreen : Color
|
|
||||||
lightGreen =
|
|
||||||
RGBA 138 226 52 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
green : Color
|
|
||||||
green =
|
|
||||||
RGBA 115 210 22 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkGreen : Color
|
|
||||||
darkGreen =
|
|
||||||
RGBA 78 154 6 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightBlue : Color
|
|
||||||
lightBlue =
|
|
||||||
RGBA 114 159 207 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
blue : Color
|
|
||||||
blue =
|
|
||||||
RGBA 52 101 164 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkBlue : Color
|
|
||||||
darkBlue =
|
|
||||||
RGBA 32 74 135 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightPurple : Color
|
|
||||||
lightPurple =
|
|
||||||
RGBA 173 127 168 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
purple : Color
|
|
||||||
purple =
|
|
||||||
RGBA 117 80 123 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkPurple : Color
|
|
||||||
darkPurple =
|
|
||||||
RGBA 92 53 102 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightBrown : Color
|
|
||||||
lightBrown =
|
|
||||||
RGBA 233 185 110 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
brown : Color
|
|
||||||
brown =
|
|
||||||
RGBA 193 125 17 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkBrown : Color
|
|
||||||
darkBrown =
|
|
||||||
RGBA 143 89 2 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
black : Color
|
|
||||||
black =
|
|
||||||
RGBA 0 0 0 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
white : Color
|
|
||||||
white =
|
|
||||||
RGBA 255 255 255 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightGrey : Color
|
|
||||||
lightGrey =
|
|
||||||
RGBA 238 238 236 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
grey : Color
|
|
||||||
grey =
|
|
||||||
RGBA 211 215 207 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkGrey : Color
|
|
||||||
darkGrey =
|
|
||||||
RGBA 186 189 182 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightGray : Color
|
|
||||||
lightGray =
|
|
||||||
RGBA 238 238 236 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
gray : Color
|
|
||||||
gray =
|
|
||||||
RGBA 211 215 207 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkGray : Color
|
|
||||||
darkGray =
|
|
||||||
RGBA 186 189 182 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
lightCharcoal : Color
|
|
||||||
lightCharcoal =
|
|
||||||
RGBA 136 138 133 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
charcoal : Color
|
|
||||||
charcoal =
|
|
||||||
RGBA 85 87 83 1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
darkCharcoal : Color
|
|
||||||
darkCharcoal =
|
|
||||||
RGBA 46 52 54 1
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
module Date exposing
|
|
||||||
( Date, fromString, toTime, fromTime
|
|
||||||
, year, month, Month(..)
|
|
||||||
, day, dayOfWeek, Day(..)
|
|
||||||
, hour, minute, second, millisecond
|
|
||||||
, now
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Library for working with dates. Email the mailing list if you encounter
|
|
||||||
issues with internationalization or locale formatting.
|
|
||||||
|
|
||||||
# Dates
|
|
||||||
@docs Date, now
|
|
||||||
|
|
||||||
# Conversions
|
|
||||||
@docs fromString, toTime, fromTime
|
|
||||||
|
|
||||||
# Extractions
|
|
||||||
@docs year, month, Month, day, dayOfWeek, Day, hour, minute, second, millisecond
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Date
|
|
||||||
import Task exposing (Task)
|
|
||||||
import Time exposing (Time)
|
|
||||||
import Result exposing (Result)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- DATES
|
|
||||||
|
|
||||||
|
|
||||||
{-| Representation of a date.
|
|
||||||
-}
|
|
||||||
type Date = Date
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the `Date` at the moment when this task is run.
|
|
||||||
-}
|
|
||||||
now : Task x Date
|
|
||||||
now =
|
|
||||||
Task.map fromTime Time.now
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- CONVERSIONS AND EXTRACTIONS
|
|
||||||
|
|
||||||
|
|
||||||
{-| Represents the days of the week.
|
|
||||||
-}
|
|
||||||
type Day = Mon | Tue | Wed | Thu | Fri | Sat | Sun
|
|
||||||
|
|
||||||
|
|
||||||
{-| Represents the month of the year.
|
|
||||||
-}
|
|
||||||
type Month
|
|
||||||
= Jan | Feb | Mar | Apr
|
|
||||||
| May | Jun | Jul | Aug
|
|
||||||
| Sep | Oct | Nov | Dec
|
|
||||||
|
|
||||||
|
|
||||||
{-| Attempt to read a date from a string.
|
|
||||||
-}
|
|
||||||
fromString : String -> Result String Date
|
|
||||||
fromString =
|
|
||||||
Native.Date.fromString
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a `Date` to a time in milliseconds.
|
|
||||||
|
|
||||||
A time is the number of milliseconds since
|
|
||||||
[the Unix epoch](http://en.wikipedia.org/wiki/Unix_time).
|
|
||||||
-}
|
|
||||||
toTime : Date -> Time
|
|
||||||
toTime =
|
|
||||||
Native.Date.toTime
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a time in milliseconds into a `Date`.
|
|
||||||
|
|
||||||
A time is the number of milliseconds since
|
|
||||||
[the Unix epoch](http://en.wikipedia.org/wiki/Unix_time).
|
|
||||||
-}
|
|
||||||
fromTime : Time -> Date
|
|
||||||
fromTime =
|
|
||||||
Native.Date.fromTime
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the year of a given date. Given the date 23 June 1990 at 11:45AM
|
|
||||||
this returns the integer `1990`.
|
|
||||||
-}
|
|
||||||
year : Date -> Int
|
|
||||||
year =
|
|
||||||
Native.Date.year
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the month of a given date. Given the date 23 June 1990 at 11:45AM
|
|
||||||
this returns the month `Jun` as defined below.
|
|
||||||
-}
|
|
||||||
month : Date -> Month
|
|
||||||
month =
|
|
||||||
Native.Date.month
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the day of a given date. Given the date 23 June 1990 at 11:45AM
|
|
||||||
this returns the integer `23`.
|
|
||||||
-}
|
|
||||||
day : Date -> Int
|
|
||||||
day =
|
|
||||||
Native.Date.day
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the day of the week for a given date. Given the date 23 June
|
|
||||||
1990 at 11:45AM this returns the day `Sat` as defined below.
|
|
||||||
-}
|
|
||||||
dayOfWeek : Date -> Day
|
|
||||||
dayOfWeek =
|
|
||||||
Native.Date.dayOfWeek
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the hour of a given date. Given the date 23 June 1990 at 11:45AM
|
|
||||||
this returns the integer `11`.
|
|
||||||
-}
|
|
||||||
hour : Date -> Int
|
|
||||||
hour =
|
|
||||||
Native.Date.hour
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the minute of a given date. Given the date 23 June 1990 at 11:45AM
|
|
||||||
this returns the integer `45`.
|
|
||||||
-}
|
|
||||||
minute : Date -> Int
|
|
||||||
minute =
|
|
||||||
Native.Date.minute
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the second of a given date. Given the date 23 June 1990 at 11:45AM
|
|
||||||
this returns the integer `0`.
|
|
||||||
-}
|
|
||||||
second : Date -> Int
|
|
||||||
second =
|
|
||||||
Native.Date.second
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the millisecond of a given date. Given the date 23 June 1990 at 11:45:30.123AM
|
|
||||||
this returns the integer `123`.
|
|
||||||
-}
|
|
||||||
millisecond : Date -> Int
|
|
||||||
millisecond =
|
|
||||||
Native.Date.millisecond
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
module Debug exposing
|
|
||||||
( log
|
|
||||||
, crash
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| This library is for investigating bugs or performance problems. It should
|
|
||||||
*not* be used in production code.
|
|
||||||
|
|
||||||
# Debugging
|
|
||||||
@docs log, crash
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Debug
|
|
||||||
|
|
||||||
|
|
||||||
{-| Log a tagged value on the developer console, and then return the value.
|
|
||||||
|
|
||||||
1 + log "number" 1 -- equals 2, logs "number: 1"
|
|
||||||
length (log "start" []) -- equals 0, logs "start: []"
|
|
||||||
|
|
||||||
Notice that `log` is not a pure function! It should *only* be used for
|
|
||||||
investigating bugs or performance problems.
|
|
||||||
-}
|
|
||||||
log : String -> a -> a
|
|
||||||
log =
|
|
||||||
Native.Debug.log
|
|
||||||
|
|
||||||
|
|
||||||
{-| Crash the program with an error message. This is an uncatchable error,
|
|
||||||
intended for code that is soon-to-be-implemented. For example, if you are
|
|
||||||
working with a large ADT and have partially completed a case expression, it may
|
|
||||||
make sense to do this:
|
|
||||||
|
|
||||||
type Entity = Ship | Fish | Captain | Seagull
|
|
||||||
|
|
||||||
drawEntity entity =
|
|
||||||
case entity of
|
|
||||||
Ship ->
|
|
||||||
...
|
|
||||||
|
|
||||||
Fish ->
|
|
||||||
...
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Debug.crash "TODO"
|
|
||||||
|
|
||||||
The Elm compiler recognizes each `Debug.crash` and when you run into it at
|
|
||||||
runtime, the error will point to the corresponding module name and line number.
|
|
||||||
For `case` expressions that ends with a wildcard pattern and a crash, it will
|
|
||||||
also show the value that snuck through. In our example, that'd be `Captain` or
|
|
||||||
`Seagull`.
|
|
||||||
|
|
||||||
**Use this if** you want to do some testing while you are partway through
|
|
||||||
writing a function.
|
|
||||||
|
|
||||||
**Do not use this if** you want to do some typical try-catch exception handling.
|
|
||||||
Use the [`Maybe`](Maybe) or [`Result`](Result) libraries instead.
|
|
||||||
-}
|
|
||||||
crash : String -> a
|
|
||||||
crash =
|
|
||||||
Native.Debug.crash
|
|
||||||
|
|
||||||
@@ -1,661 +0,0 @@
|
|||||||
module Dict exposing
|
|
||||||
( Dict
|
|
||||||
, empty, singleton, insert, update
|
|
||||||
, isEmpty, get, remove, member, size
|
|
||||||
, filter
|
|
||||||
, partition
|
|
||||||
, foldl, foldr, map
|
|
||||||
, union, intersect, diff, merge
|
|
||||||
, keys, values
|
|
||||||
, toList, fromList
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| A dictionary mapping unique keys to values. The keys can be any comparable
|
|
||||||
type. This includes `Int`, `Float`, `Time`, `Char`, `String`, and tuples or
|
|
||||||
lists of comparable types.
|
|
||||||
|
|
||||||
Insert, remove, and query operations all take *O(log n)* time.
|
|
||||||
|
|
||||||
# Dictionaries
|
|
||||||
@docs Dict
|
|
||||||
|
|
||||||
# Build
|
|
||||||
@docs empty, singleton, insert, update, remove
|
|
||||||
|
|
||||||
# Query
|
|
||||||
@docs isEmpty, member, get, size
|
|
||||||
|
|
||||||
# Lists
|
|
||||||
@docs keys, values, toList, fromList
|
|
||||||
|
|
||||||
# Transform
|
|
||||||
@docs map, foldl, foldr, filter, partition
|
|
||||||
|
|
||||||
# Combine
|
|
||||||
@docs union, intersect, diff, merge
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Maybe exposing (..)
|
|
||||||
import List exposing (..)
|
|
||||||
import Native.Debug
|
|
||||||
import String
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- DICTIONARIES
|
|
||||||
|
|
||||||
|
|
||||||
-- BBlack and NBlack should only be used during the deletion
|
|
||||||
-- algorithm. Any other occurrence is a bug and should fail an assert.
|
|
||||||
type NColor
|
|
||||||
= Red
|
|
||||||
| Black
|
|
||||||
| BBlack -- Double Black, counts as 2 blacks for the invariant
|
|
||||||
| NBlack -- Negative Black, counts as -1 blacks for the invariant
|
|
||||||
|
|
||||||
|
|
||||||
type LeafColor
|
|
||||||
= LBlack
|
|
||||||
| LBBlack -- Double Black, counts as 2
|
|
||||||
|
|
||||||
|
|
||||||
{-| A dictionary of keys and values. So a `(Dict String User)` is a dictionary
|
|
||||||
that lets you look up a `String` (such as user names) and find the associated
|
|
||||||
`User`.
|
|
||||||
-}
|
|
||||||
type Dict k v
|
|
||||||
= RBNode_elm_builtin NColor k v (Dict k v) (Dict k v)
|
|
||||||
| RBEmpty_elm_builtin LeafColor
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create an empty dictionary. -}
|
|
||||||
empty : Dict k v
|
|
||||||
empty =
|
|
||||||
RBEmpty_elm_builtin LBlack
|
|
||||||
|
|
||||||
|
|
||||||
maxWithDefault : k -> v -> Dict k v -> (k, v)
|
|
||||||
maxWithDefault k v r =
|
|
||||||
case r of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
(k, v)
|
|
||||||
|
|
||||||
RBNode_elm_builtin _ kr vr _ rr ->
|
|
||||||
maxWithDefault kr vr rr
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the value associated with a key. If the key is not found, return
|
|
||||||
`Nothing`. This is useful when you are not sure if a key will be in the
|
|
||||||
dictionary.
|
|
||||||
|
|
||||||
animals = fromList [ ("Tom", Cat), ("Jerry", Mouse) ]
|
|
||||||
|
|
||||||
get "Tom" animals == Just Cat
|
|
||||||
get "Jerry" animals == Just Mouse
|
|
||||||
get "Spike" animals == Nothing
|
|
||||||
|
|
||||||
-}
|
|
||||||
get : comparable -> Dict comparable v -> Maybe v
|
|
||||||
get targetKey dict =
|
|
||||||
case dict of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
RBNode_elm_builtin _ key value left right ->
|
|
||||||
case compare targetKey key of
|
|
||||||
LT ->
|
|
||||||
get targetKey left
|
|
||||||
|
|
||||||
EQ ->
|
|
||||||
Just value
|
|
||||||
|
|
||||||
GT ->
|
|
||||||
get targetKey right
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if a key is in a dictionary. -}
|
|
||||||
member : comparable -> Dict comparable v -> Bool
|
|
||||||
member key dict =
|
|
||||||
case get key dict of
|
|
||||||
Just _ ->
|
|
||||||
True
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
False
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine the number of key-value pairs in the dictionary. -}
|
|
||||||
size : Dict k v -> Int
|
|
||||||
size dict =
|
|
||||||
sizeHelp 0 dict
|
|
||||||
|
|
||||||
|
|
||||||
sizeHelp : Int -> Dict k v -> Int
|
|
||||||
sizeHelp n dict =
|
|
||||||
case dict of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
n
|
|
||||||
|
|
||||||
RBNode_elm_builtin _ _ _ left right ->
|
|
||||||
sizeHelp (sizeHelp (n+1) right) left
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if a dictionary is empty.
|
|
||||||
|
|
||||||
isEmpty empty == True
|
|
||||||
-}
|
|
||||||
isEmpty : Dict k v -> Bool
|
|
||||||
isEmpty dict =
|
|
||||||
dict == empty
|
|
||||||
|
|
||||||
|
|
||||||
{- The actual pattern match here is somewhat lax. If it is given invalid input,
|
|
||||||
it will do the wrong thing. The expected behavior is:
|
|
||||||
|
|
||||||
red node => black node
|
|
||||||
black node => same
|
|
||||||
bblack node => xxx
|
|
||||||
nblack node => xxx
|
|
||||||
|
|
||||||
black leaf => same
|
|
||||||
bblack leaf => xxx
|
|
||||||
-}
|
|
||||||
ensureBlackRoot : Dict k v -> Dict k v
|
|
||||||
ensureBlackRoot dict =
|
|
||||||
case dict of
|
|
||||||
RBNode_elm_builtin Red key value left right ->
|
|
||||||
RBNode_elm_builtin Black key value left right
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
dict
|
|
||||||
|
|
||||||
|
|
||||||
{-| Insert a key-value pair into a dictionary. Replaces value when there is
|
|
||||||
a collision. -}
|
|
||||||
insert : comparable -> v -> Dict comparable v -> Dict comparable v
|
|
||||||
insert key value dict =
|
|
||||||
update key (always (Just value)) dict
|
|
||||||
|
|
||||||
|
|
||||||
{-| Remove a key-value pair from a dictionary. If the key is not found,
|
|
||||||
no changes are made. -}
|
|
||||||
remove : comparable -> Dict comparable v -> Dict comparable v
|
|
||||||
remove key dict =
|
|
||||||
update key (always Nothing) dict
|
|
||||||
|
|
||||||
|
|
||||||
type Flag = Insert | Remove | Same
|
|
||||||
|
|
||||||
|
|
||||||
{-| Update the value of a dictionary for a specific key with a given function. -}
|
|
||||||
update : comparable -> (Maybe v -> Maybe v) -> Dict comparable v -> Dict comparable v
|
|
||||||
update k alter dict =
|
|
||||||
let
|
|
||||||
up dict =
|
|
||||||
case dict of
|
|
||||||
-- expecting only black nodes, never double black nodes here
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
case alter Nothing of
|
|
||||||
Nothing ->
|
|
||||||
(Same, empty)
|
|
||||||
|
|
||||||
Just v ->
|
|
||||||
(Insert, RBNode_elm_builtin Red k v empty empty)
|
|
||||||
|
|
||||||
RBNode_elm_builtin clr key value left right ->
|
|
||||||
case compare k key of
|
|
||||||
EQ ->
|
|
||||||
case alter (Just value) of
|
|
||||||
Nothing ->
|
|
||||||
(Remove, rem clr left right)
|
|
||||||
|
|
||||||
Just newValue ->
|
|
||||||
(Same, RBNode_elm_builtin clr key newValue left right)
|
|
||||||
|
|
||||||
LT ->
|
|
||||||
let (flag, newLeft) = up left in
|
|
||||||
case flag of
|
|
||||||
Same ->
|
|
||||||
(Same, RBNode_elm_builtin clr key value newLeft right)
|
|
||||||
|
|
||||||
Insert ->
|
|
||||||
(Insert, balance clr key value newLeft right)
|
|
||||||
|
|
||||||
Remove ->
|
|
||||||
(Remove, bubble clr key value newLeft right)
|
|
||||||
|
|
||||||
GT ->
|
|
||||||
let (flag, newRight) = up right in
|
|
||||||
case flag of
|
|
||||||
Same ->
|
|
||||||
(Same, RBNode_elm_builtin clr key value left newRight)
|
|
||||||
|
|
||||||
Insert ->
|
|
||||||
(Insert, balance clr key value left newRight)
|
|
||||||
|
|
||||||
Remove ->
|
|
||||||
(Remove, bubble clr key value left newRight)
|
|
||||||
|
|
||||||
(flag, updatedDict) =
|
|
||||||
up dict
|
|
||||||
in
|
|
||||||
case flag of
|
|
||||||
Same ->
|
|
||||||
updatedDict
|
|
||||||
|
|
||||||
Insert ->
|
|
||||||
ensureBlackRoot updatedDict
|
|
||||||
|
|
||||||
Remove ->
|
|
||||||
blacken updatedDict
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a dictionary with one key-value pair. -}
|
|
||||||
singleton : comparable -> v -> Dict comparable v
|
|
||||||
singleton key value =
|
|
||||||
insert key value empty
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- HELPERS
|
|
||||||
|
|
||||||
|
|
||||||
isBBlack : Dict k v -> Bool
|
|
||||||
isBBlack dict =
|
|
||||||
case dict of
|
|
||||||
RBNode_elm_builtin BBlack _ _ _ _ ->
|
|
||||||
True
|
|
||||||
|
|
||||||
RBEmpty_elm_builtin LBBlack ->
|
|
||||||
True
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
False
|
|
||||||
|
|
||||||
|
|
||||||
moreBlack : NColor -> NColor
|
|
||||||
moreBlack color =
|
|
||||||
case color of
|
|
||||||
Black ->
|
|
||||||
BBlack
|
|
||||||
|
|
||||||
Red ->
|
|
||||||
Black
|
|
||||||
|
|
||||||
NBlack ->
|
|
||||||
Red
|
|
||||||
|
|
||||||
BBlack ->
|
|
||||||
Native.Debug.crash "Can't make a double black node more black!"
|
|
||||||
|
|
||||||
|
|
||||||
lessBlack : NColor -> NColor
|
|
||||||
lessBlack color =
|
|
||||||
case color of
|
|
||||||
BBlack ->
|
|
||||||
Black
|
|
||||||
|
|
||||||
Black ->
|
|
||||||
Red
|
|
||||||
|
|
||||||
Red ->
|
|
||||||
NBlack
|
|
||||||
|
|
||||||
NBlack ->
|
|
||||||
Native.Debug.crash "Can't make a negative black node less black!"
|
|
||||||
|
|
||||||
|
|
||||||
{- The actual pattern match here is somewhat lax. If it is given invalid input,
|
|
||||||
it will do the wrong thing. The expected behavior is:
|
|
||||||
|
|
||||||
node => less black node
|
|
||||||
|
|
||||||
bblack leaf => black leaf
|
|
||||||
black leaf => xxx
|
|
||||||
-}
|
|
||||||
lessBlackTree : Dict k v -> Dict k v
|
|
||||||
lessBlackTree dict =
|
|
||||||
case dict of
|
|
||||||
RBNode_elm_builtin c k v l r ->
|
|
||||||
RBNode_elm_builtin (lessBlack c) k v l r
|
|
||||||
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
RBEmpty_elm_builtin LBlack
|
|
||||||
|
|
||||||
|
|
||||||
reportRemBug : String -> NColor -> String -> String -> a
|
|
||||||
reportRemBug msg c lgot rgot =
|
|
||||||
Native.Debug.crash <|
|
|
||||||
String.concat
|
|
||||||
[ "Internal red-black tree invariant violated, expected "
|
|
||||||
, msg, " and got ", toString c, "/", lgot, "/", rgot
|
|
||||||
, "\nPlease report this bug to <https://github.com/elm-lang/core/issues>"
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
-- Remove the top node from the tree, may leave behind BBlacks
|
|
||||||
rem : NColor -> Dict k v -> Dict k v -> Dict k v
|
|
||||||
rem color left right =
|
|
||||||
case (left, right) of
|
|
||||||
(RBEmpty_elm_builtin _, RBEmpty_elm_builtin _) ->
|
|
||||||
case color of
|
|
||||||
Red ->
|
|
||||||
RBEmpty_elm_builtin LBlack
|
|
||||||
|
|
||||||
Black ->
|
|
||||||
RBEmpty_elm_builtin LBBlack
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Native.Debug.crash "cannot have bblack or nblack nodes at this point"
|
|
||||||
|
|
||||||
(RBEmpty_elm_builtin cl, RBNode_elm_builtin cr k v l r) ->
|
|
||||||
case (color, cl, cr) of
|
|
||||||
(Black, LBlack, Red) ->
|
|
||||||
RBNode_elm_builtin Black k v l r
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
reportRemBug "Black/LBlack/Red" color (toString cl) (toString cr)
|
|
||||||
|
|
||||||
(RBNode_elm_builtin cl k v l r, RBEmpty_elm_builtin cr) ->
|
|
||||||
case (color, cl, cr) of
|
|
||||||
(Black, Red, LBlack) ->
|
|
||||||
RBNode_elm_builtin Black k v l r
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
reportRemBug "Black/Red/LBlack" color (toString cl) (toString cr)
|
|
||||||
|
|
||||||
-- l and r are both RBNodes
|
|
||||||
(RBNode_elm_builtin cl kl vl ll rl, RBNode_elm_builtin _ _ _ _ _) ->
|
|
||||||
let
|
|
||||||
(k, v) =
|
|
||||||
maxWithDefault kl vl rl
|
|
||||||
|
|
||||||
newLeft =
|
|
||||||
removeMax cl kl vl ll rl
|
|
||||||
in
|
|
||||||
bubble color k v newLeft right
|
|
||||||
|
|
||||||
|
|
||||||
-- Kills a BBlack or moves it upward, may leave behind NBlack
|
|
||||||
bubble : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
|
|
||||||
bubble c k v l r =
|
|
||||||
if isBBlack l || isBBlack r then
|
|
||||||
balance (moreBlack c) k v (lessBlackTree l) (lessBlackTree r)
|
|
||||||
|
|
||||||
else
|
|
||||||
RBNode_elm_builtin c k v l r
|
|
||||||
|
|
||||||
|
|
||||||
-- Removes rightmost node, may leave root as BBlack
|
|
||||||
removeMax : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
|
|
||||||
removeMax c k v l r =
|
|
||||||
case r of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
rem c l r
|
|
||||||
|
|
||||||
RBNode_elm_builtin cr kr vr lr rr ->
|
|
||||||
bubble c k v l (removeMax cr kr vr lr rr)
|
|
||||||
|
|
||||||
|
|
||||||
-- generalized tree balancing act
|
|
||||||
balance : NColor -> k -> v -> Dict k v -> Dict k v -> Dict k v
|
|
||||||
balance c k v l r =
|
|
||||||
let
|
|
||||||
tree =
|
|
||||||
RBNode_elm_builtin c k v l r
|
|
||||||
in
|
|
||||||
if blackish tree then
|
|
||||||
balanceHelp tree
|
|
||||||
|
|
||||||
else
|
|
||||||
tree
|
|
||||||
|
|
||||||
|
|
||||||
blackish : Dict k v -> Bool
|
|
||||||
blackish t =
|
|
||||||
case t of
|
|
||||||
RBNode_elm_builtin c _ _ _ _ ->
|
|
||||||
c == Black || c == BBlack
|
|
||||||
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
True
|
|
||||||
|
|
||||||
|
|
||||||
balanceHelp : Dict k v -> Dict k v
|
|
||||||
balanceHelp tree =
|
|
||||||
case tree of
|
|
||||||
-- double red: left, left
|
|
||||||
RBNode_elm_builtin col zk zv (RBNode_elm_builtin Red yk yv (RBNode_elm_builtin Red xk xv a b) c) d ->
|
|
||||||
balancedTree col xk xv yk yv zk zv a b c d
|
|
||||||
|
|
||||||
-- double red: left, right
|
|
||||||
RBNode_elm_builtin col zk zv (RBNode_elm_builtin Red xk xv a (RBNode_elm_builtin Red yk yv b c)) d ->
|
|
||||||
balancedTree col xk xv yk yv zk zv a b c d
|
|
||||||
|
|
||||||
-- double red: right, left
|
|
||||||
RBNode_elm_builtin col xk xv a (RBNode_elm_builtin Red zk zv (RBNode_elm_builtin Red yk yv b c) d) ->
|
|
||||||
balancedTree col xk xv yk yv zk zv a b c d
|
|
||||||
|
|
||||||
-- double red: right, right
|
|
||||||
RBNode_elm_builtin col xk xv a (RBNode_elm_builtin Red yk yv b (RBNode_elm_builtin Red zk zv c d)) ->
|
|
||||||
balancedTree col xk xv yk yv zk zv a b c d
|
|
||||||
|
|
||||||
-- handle double blacks
|
|
||||||
RBNode_elm_builtin BBlack xk xv a (RBNode_elm_builtin NBlack zk zv (RBNode_elm_builtin Black yk yv b c) (RBNode_elm_builtin Black _ _ _ _ as d)) ->
|
|
||||||
RBNode_elm_builtin Black yk yv (RBNode_elm_builtin Black xk xv a b) (balance Black zk zv c (redden d))
|
|
||||||
|
|
||||||
RBNode_elm_builtin BBlack zk zv (RBNode_elm_builtin NBlack xk xv (RBNode_elm_builtin Black _ _ _ _ as a) (RBNode_elm_builtin Black yk yv b c)) d ->
|
|
||||||
RBNode_elm_builtin Black yk yv (balance Black xk xv (redden a) b) (RBNode_elm_builtin Black zk zv c d)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
tree
|
|
||||||
|
|
||||||
|
|
||||||
balancedTree : NColor -> k -> v -> k -> v -> k -> v -> Dict k v -> Dict k v -> Dict k v -> Dict k v -> Dict k v
|
|
||||||
balancedTree col xk xv yk yv zk zv a b c d =
|
|
||||||
RBNode_elm_builtin
|
|
||||||
(lessBlack col)
|
|
||||||
yk
|
|
||||||
yv
|
|
||||||
(RBNode_elm_builtin Black xk xv a b)
|
|
||||||
(RBNode_elm_builtin Black zk zv c d)
|
|
||||||
|
|
||||||
|
|
||||||
-- make the top node black
|
|
||||||
blacken : Dict k v -> Dict k v
|
|
||||||
blacken t =
|
|
||||||
case t of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
RBEmpty_elm_builtin LBlack
|
|
||||||
|
|
||||||
RBNode_elm_builtin _ k v l r ->
|
|
||||||
RBNode_elm_builtin Black k v l r
|
|
||||||
|
|
||||||
|
|
||||||
-- make the top node red
|
|
||||||
redden : Dict k v -> Dict k v
|
|
||||||
redden t =
|
|
||||||
case t of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
Native.Debug.crash "can't make a Leaf red"
|
|
||||||
|
|
||||||
RBNode_elm_builtin _ k v l r ->
|
|
||||||
RBNode_elm_builtin Red k v l r
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- COMBINE
|
|
||||||
|
|
||||||
|
|
||||||
{-| Combine two dictionaries. If there is a collision, preference is given
|
|
||||||
to the first dictionary.
|
|
||||||
-}
|
|
||||||
union : Dict comparable v -> Dict comparable v -> Dict comparable v
|
|
||||||
union t1 t2 =
|
|
||||||
foldl insert t2 t1
|
|
||||||
|
|
||||||
|
|
||||||
{-| Keep a key-value pair when its key appears in the second dictionary.
|
|
||||||
Preference is given to values in the first dictionary.
|
|
||||||
-}
|
|
||||||
intersect : Dict comparable v -> Dict comparable v -> Dict comparable v
|
|
||||||
intersect t1 t2 =
|
|
||||||
filter (\k _ -> member k t2) t1
|
|
||||||
|
|
||||||
|
|
||||||
{-| Keep a key-value pair when its key does not appear in the second dictionary.
|
|
||||||
-}
|
|
||||||
diff : Dict comparable v -> Dict comparable v -> Dict comparable v
|
|
||||||
diff t1 t2 =
|
|
||||||
foldl (\k v t -> remove k t) t1 t2
|
|
||||||
|
|
||||||
|
|
||||||
{-| The most general way of combining two dictionaries. You provide three
|
|
||||||
accumulators for when a given key appears:
|
|
||||||
|
|
||||||
1. Only in the left dictionary.
|
|
||||||
2. In both dictionaries.
|
|
||||||
3. Only in the right dictionary.
|
|
||||||
|
|
||||||
You then traverse all the keys from lowest to highest, building up whatever
|
|
||||||
you want.
|
|
||||||
-}
|
|
||||||
merge
|
|
||||||
: (comparable -> a -> result -> result)
|
|
||||||
-> (comparable -> a -> b -> result -> result)
|
|
||||||
-> (comparable -> b -> result -> result)
|
|
||||||
-> Dict comparable a
|
|
||||||
-> Dict comparable b
|
|
||||||
-> result
|
|
||||||
-> result
|
|
||||||
merge leftStep bothStep rightStep leftDict rightDict initialResult =
|
|
||||||
let
|
|
||||||
stepState rKey rValue (list, result) =
|
|
||||||
case list of
|
|
||||||
[] ->
|
|
||||||
(list, rightStep rKey rValue result)
|
|
||||||
|
|
||||||
(lKey, lValue) :: rest ->
|
|
||||||
if lKey < rKey then
|
|
||||||
stepState rKey rValue (rest, leftStep lKey lValue result)
|
|
||||||
|
|
||||||
else if lKey > rKey then
|
|
||||||
(list, rightStep rKey rValue result)
|
|
||||||
|
|
||||||
else
|
|
||||||
(rest, bothStep lKey lValue rValue result)
|
|
||||||
|
|
||||||
(leftovers, intermediateResult) =
|
|
||||||
foldl stepState (toList leftDict, initialResult) rightDict
|
|
||||||
in
|
|
||||||
List.foldl (\(k,v) result -> leftStep k v result) intermediateResult leftovers
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- TRANSFORM
|
|
||||||
|
|
||||||
|
|
||||||
{-| Apply a function to all values in a dictionary.
|
|
||||||
-}
|
|
||||||
map : (comparable -> a -> b) -> Dict comparable a -> Dict comparable b
|
|
||||||
map f dict =
|
|
||||||
case dict of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
RBEmpty_elm_builtin LBlack
|
|
||||||
|
|
||||||
RBNode_elm_builtin clr key value left right ->
|
|
||||||
RBNode_elm_builtin clr key (f key value) (map f left) (map f right)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Fold over the key-value pairs in a dictionary, in order from lowest
|
|
||||||
key to highest key.
|
|
||||||
-}
|
|
||||||
foldl : (comparable -> v -> b -> b) -> b -> Dict comparable v -> b
|
|
||||||
foldl f acc dict =
|
|
||||||
case dict of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
acc
|
|
||||||
|
|
||||||
RBNode_elm_builtin _ key value left right ->
|
|
||||||
foldl f (f key value (foldl f acc left)) right
|
|
||||||
|
|
||||||
|
|
||||||
{-| Fold over the key-value pairs in a dictionary, in order from highest
|
|
||||||
key to lowest key.
|
|
||||||
-}
|
|
||||||
foldr : (comparable -> v -> b -> b) -> b -> Dict comparable v -> b
|
|
||||||
foldr f acc t =
|
|
||||||
case t of
|
|
||||||
RBEmpty_elm_builtin _ ->
|
|
||||||
acc
|
|
||||||
|
|
||||||
RBNode_elm_builtin _ key value left right ->
|
|
||||||
foldr f (f key value (foldr f acc right)) left
|
|
||||||
|
|
||||||
|
|
||||||
{-| Keep a key-value pair when it satisfies a predicate. -}
|
|
||||||
filter : (comparable -> v -> Bool) -> Dict comparable v -> Dict comparable v
|
|
||||||
filter predicate dictionary =
|
|
||||||
let
|
|
||||||
add key value dict =
|
|
||||||
if predicate key value then
|
|
||||||
insert key value dict
|
|
||||||
|
|
||||||
else
|
|
||||||
dict
|
|
||||||
in
|
|
||||||
foldl add empty dictionary
|
|
||||||
|
|
||||||
|
|
||||||
{-| Partition a dictionary according to a predicate. The first dictionary
|
|
||||||
contains all key-value pairs which satisfy the predicate, and the second
|
|
||||||
contains the rest.
|
|
||||||
-}
|
|
||||||
partition : (comparable -> v -> Bool) -> Dict comparable v -> (Dict comparable v, Dict comparable v)
|
|
||||||
partition predicate dict =
|
|
||||||
let
|
|
||||||
add key value (t1, t2) =
|
|
||||||
if predicate key value then
|
|
||||||
(insert key value t1, t2)
|
|
||||||
|
|
||||||
else
|
|
||||||
(t1, insert key value t2)
|
|
||||||
in
|
|
||||||
foldl add (empty, empty) dict
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- LISTS
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get all of the keys in a dictionary, sorted from lowest to highest.
|
|
||||||
|
|
||||||
keys (fromList [(0,"Alice"),(1,"Bob")]) == [0,1]
|
|
||||||
-}
|
|
||||||
keys : Dict comparable v -> List comparable
|
|
||||||
keys dict =
|
|
||||||
foldr (\key value keyList -> key :: keyList) [] dict
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get all of the values in a dictionary, in the order of their keys.
|
|
||||||
|
|
||||||
values (fromList [(0,"Alice"),(1,"Bob")]) == ["Alice", "Bob"]
|
|
||||||
-}
|
|
||||||
values : Dict comparable v -> List v
|
|
||||||
values dict =
|
|
||||||
foldr (\key value valueList -> value :: valueList) [] dict
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a dictionary into an association list of key-value pairs, sorted by keys. -}
|
|
||||||
toList : Dict comparable v -> List (comparable,v)
|
|
||||||
toList dict =
|
|
||||||
foldr (\key value list -> (key,value) :: list) [] dict
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert an association list into a dictionary. -}
|
|
||||||
fromList : List (comparable,v) -> Dict comparable v
|
|
||||||
fromList assocs =
|
|
||||||
List.foldl (\(key,value) dict -> insert key value dict) empty assocs
|
|
||||||
@@ -1,520 +0,0 @@
|
|||||||
module Json.Decode exposing
|
|
||||||
( Decoder, string, bool, int, float
|
|
||||||
, nullable, list, array, dict, keyValuePairs
|
|
||||||
, field, at, index
|
|
||||||
, maybe, oneOf
|
|
||||||
, decodeString, decodeValue, Value
|
|
||||||
, map, map2, map3, map4, map5, map6, map7, map8
|
|
||||||
, lazy, value, null, succeed, fail, andThen
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Turn JSON values into Elm values. Definitely check out this [intro to
|
|
||||||
JSON decoders][guide] to get a feel for how this library works!
|
|
||||||
|
|
||||||
[guide]: https://guide.elm-lang.org/interop/json.html
|
|
||||||
|
|
||||||
# Primitives
|
|
||||||
@docs Decoder, string, bool, int, float
|
|
||||||
|
|
||||||
# Data Structures
|
|
||||||
@docs nullable, list, array, dict, keyValuePairs
|
|
||||||
|
|
||||||
# Object Primitives
|
|
||||||
@docs field, at, index
|
|
||||||
|
|
||||||
# Inconsistent Structure
|
|
||||||
@docs maybe, oneOf
|
|
||||||
|
|
||||||
# Run Decoders
|
|
||||||
@docs decodeString, decodeValue, Value
|
|
||||||
|
|
||||||
# Mapping
|
|
||||||
|
|
||||||
**Note:** If you run out of map functions, take a look at [elm-decode-pipeline][pipe]
|
|
||||||
which makes it easier to handle large objects, but produces lower quality type
|
|
||||||
errors.
|
|
||||||
|
|
||||||
[pipe]: http://package.elm-lang.org/packages/NoRedInk/elm-decode-pipeline/latest
|
|
||||||
|
|
||||||
@docs map, map2, map3, map4, map5, map6, map7, map8
|
|
||||||
|
|
||||||
# Fancy Decoding
|
|
||||||
@docs lazy, value, null, succeed, fail, andThen
|
|
||||||
-}
|
|
||||||
|
|
||||||
|
|
||||||
import Array exposing (Array)
|
|
||||||
import Dict exposing (Dict)
|
|
||||||
import Json.Encode as JsEncode
|
|
||||||
import List
|
|
||||||
import Maybe exposing (Maybe(..))
|
|
||||||
import Result exposing (Result(..))
|
|
||||||
import Native.Json
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- PRIMITIVES
|
|
||||||
|
|
||||||
|
|
||||||
{-| A value that knows how to decode JSON values.
|
|
||||||
-}
|
|
||||||
type Decoder a = Decoder
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON string into an Elm `String`.
|
|
||||||
|
|
||||||
decodeString string "true" == Err ...
|
|
||||||
decodeString string "42" == Err ...
|
|
||||||
decodeString string "3.14" == Err ...
|
|
||||||
decodeString string "\"hello\"" == Ok "hello"
|
|
||||||
decodeString string "{ \"hello\": 42 }" == Err ...
|
|
||||||
-}
|
|
||||||
string : Decoder String
|
|
||||||
string =
|
|
||||||
Native.Json.decodePrimitive "string"
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON boolean into an Elm `Bool`.
|
|
||||||
|
|
||||||
decodeString bool "true" == Ok True
|
|
||||||
decodeString bool "42" == Err ...
|
|
||||||
decodeString bool "3.14" == Err ...
|
|
||||||
decodeString bool "\"hello\"" == Err ...
|
|
||||||
decodeString bool "{ \"hello\": 42 }" == Err ...
|
|
||||||
-}
|
|
||||||
bool : Decoder Bool
|
|
||||||
bool =
|
|
||||||
Native.Json.decodePrimitive "bool"
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON number into an Elm `Int`.
|
|
||||||
|
|
||||||
decodeString int "true" == Err ...
|
|
||||||
decodeString int "42" == Ok 42
|
|
||||||
decodeString int "3.14" == Err ...
|
|
||||||
decodeString int "\"hello\"" == Err ...
|
|
||||||
decodeString int "{ \"hello\": 42 }" == Err ...
|
|
||||||
-}
|
|
||||||
int : Decoder Int
|
|
||||||
int =
|
|
||||||
Native.Json.decodePrimitive "int"
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON number into an Elm `Float`.
|
|
||||||
|
|
||||||
decodeString float "true" == Err ..
|
|
||||||
decodeString float "42" == Ok 42
|
|
||||||
decodeString float "3.14" == Ok 3.14
|
|
||||||
decodeString float "\"hello\"" == Err ...
|
|
||||||
decodeString float "{ \"hello\": 42 }" == Err ...
|
|
||||||
-}
|
|
||||||
float : Decoder Float
|
|
||||||
float =
|
|
||||||
Native.Json.decodePrimitive "float"
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- DATA STRUCTURES
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a nullable JSON value into an Elm value.
|
|
||||||
|
|
||||||
decodeString (nullable int) "13" == Ok (Just 13)
|
|
||||||
decodeString (nullable int) "42" == Ok (Just 42)
|
|
||||||
decodeString (nullable int) "null" == Ok Nothing
|
|
||||||
decodeString (nullable int) "true" == Err ..
|
|
||||||
-}
|
|
||||||
nullable : Decoder a -> Decoder (Maybe a)
|
|
||||||
nullable decoder =
|
|
||||||
oneOf
|
|
||||||
[ null Nothing
|
|
||||||
, map Just decoder
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON array into an Elm `List`.
|
|
||||||
|
|
||||||
decodeString (list int) "[1,2,3]" == Ok [1,2,3]
|
|
||||||
decodeString (list bool) "[true,false]" == Ok [True,False]
|
|
||||||
-}
|
|
||||||
list : Decoder a -> Decoder (List a)
|
|
||||||
list decoder =
|
|
||||||
Native.Json.decodeContainer "list" decoder
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON array into an Elm `Array`.
|
|
||||||
|
|
||||||
decodeString (array int) "[1,2,3]" == Ok (Array.fromList [1,2,3])
|
|
||||||
decodeString (array bool) "[true,false]" == Ok (Array.fromList [True,False])
|
|
||||||
-}
|
|
||||||
array : Decoder a -> Decoder (Array a)
|
|
||||||
array decoder =
|
|
||||||
Native.Json.decodeContainer "array" decoder
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON object into an Elm `Dict`.
|
|
||||||
|
|
||||||
decodeString (dict int) "{ \"alice\": 42, \"bob\": 99 }"
|
|
||||||
== Dict.fromList [("alice", 42), ("bob", 99)]
|
|
||||||
-}
|
|
||||||
dict : Decoder a -> Decoder (Dict String a)
|
|
||||||
dict decoder =
|
|
||||||
map Dict.fromList (keyValuePairs decoder)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON object into an Elm `List` of pairs.
|
|
||||||
|
|
||||||
decodeString (keyValuePairs int) "{ \"alice\": 42, \"bob\": 99 }"
|
|
||||||
== [("alice", 42), ("bob", 99)]
|
|
||||||
-}
|
|
||||||
keyValuePairs : Decoder a -> Decoder (List (String, a))
|
|
||||||
keyValuePairs =
|
|
||||||
Native.Json.decodeKeyValuePairs
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- OBJECT PRIMITIVES
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON object, requiring a particular field.
|
|
||||||
|
|
||||||
decodeString (field "x" int) "{ \"x\": 3 }" == Ok 3
|
|
||||||
decodeString (field "x" int) "{ \"x\": 3, \"y\": 4 }" == Ok 3
|
|
||||||
decodeString (field "x" int) "{ \"x\": true }" == Err ...
|
|
||||||
decodeString (field "x" int) "{ \"y\": 4 }" == Err ...
|
|
||||||
|
|
||||||
decodeString (field "name" string) "{ \"name\": \"tom\" }" == Ok "tom"
|
|
||||||
|
|
||||||
The object *can* have other fields. Lots of them! The only thing this decoder
|
|
||||||
cares about is if `x` is present and that the value there is an `Int`.
|
|
||||||
|
|
||||||
Check out [`map2`](#map2) to see how to decode multiple fields!
|
|
||||||
-}
|
|
||||||
field : String -> Decoder a -> Decoder a
|
|
||||||
field =
|
|
||||||
Native.Json.decodeField
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a nested JSON object, requiring certain fields.
|
|
||||||
|
|
||||||
json = """{ "person": { "name": "tom", "age": 42 } }"""
|
|
||||||
|
|
||||||
decodeString (at ["person", "name"] string) json == Ok "tom"
|
|
||||||
decodeString (at ["person", "age" ] int ) json == Ok "42
|
|
||||||
|
|
||||||
This is really just a shorthand for saying things like:
|
|
||||||
|
|
||||||
field "person" (field "name" string) == at ["person","name"] string
|
|
||||||
-}
|
|
||||||
at : List String -> Decoder a -> Decoder a
|
|
||||||
at fields decoder =
|
|
||||||
List.foldr field decoder fields
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a JSON array, requiring a particular index.
|
|
||||||
|
|
||||||
json = """[ "alice", "bob", "chuck" ]"""
|
|
||||||
|
|
||||||
decodeString (index 0 string) json == Ok "alice"
|
|
||||||
decodeString (index 1 string) json == Ok "bob"
|
|
||||||
decodeString (index 2 string) json == Ok "chuck"
|
|
||||||
decodeString (index 3 string) json == Err ...
|
|
||||||
-}
|
|
||||||
index : Int -> Decoder a -> Decoder a
|
|
||||||
index =
|
|
||||||
Native.Json.decodeIndex
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- WEIRD STRUCTURE
|
|
||||||
|
|
||||||
|
|
||||||
{-| Helpful for dealing with optional fields. Here are a few slightly different
|
|
||||||
examples:
|
|
||||||
|
|
||||||
json = """{ "name": "tom", "age": 42 }"""
|
|
||||||
|
|
||||||
decodeString (maybe (field "age" int )) json == Ok (Just 42)
|
|
||||||
decodeString (maybe (field "name" int )) json == Ok Nothing
|
|
||||||
decodeString (maybe (field "height" float)) json == Ok Nothing
|
|
||||||
|
|
||||||
decodeString (field "age" (maybe int )) json == Ok (Just 42)
|
|
||||||
decodeString (field "name" (maybe int )) json == Ok Nothing
|
|
||||||
decodeString (field "height" (maybe float)) json == Err ...
|
|
||||||
|
|
||||||
Notice the last example! It is saying we *must* have a field named `height` and
|
|
||||||
the content *may* be a float. There is no `height` field, so the decoder fails.
|
|
||||||
|
|
||||||
Point is, `maybe` will make exactly what it contains conditional. For optional
|
|
||||||
fields, this means you probably want it *outside* a use of `field` or `at`.
|
|
||||||
-}
|
|
||||||
maybe : Decoder a -> Decoder (Maybe a)
|
|
||||||
maybe decoder =
|
|
||||||
Native.Json.decodeContainer "maybe" decoder
|
|
||||||
|
|
||||||
|
|
||||||
{-| Try a bunch of different decoders. This can be useful if the JSON may come
|
|
||||||
in a couple different formats. For example, say you want to read an array of
|
|
||||||
numbers, but some of them are `null`.
|
|
||||||
|
|
||||||
import String
|
|
||||||
|
|
||||||
badInt : Decoder Int
|
|
||||||
badInt =
|
|
||||||
oneOf [ int, null 0 ]
|
|
||||||
|
|
||||||
-- decodeString (list badInt) "[1,2,null,4]" == Ok [1,2,0,4]
|
|
||||||
|
|
||||||
Why would someone generate JSON like this? Questions like this are not good
|
|
||||||
for your health. The point is that you can use `oneOf` to handle situations
|
|
||||||
like this!
|
|
||||||
|
|
||||||
You could also use `oneOf` to help version your data. Try the latest format,
|
|
||||||
then a few older ones that you still support. You could use `andThen` to be
|
|
||||||
even more particular if you wanted.
|
|
||||||
-}
|
|
||||||
oneOf : List (Decoder a) -> Decoder a
|
|
||||||
oneOf =
|
|
||||||
Native.Json.oneOf
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- MAPPING
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform a decoder. Maybe you just want to know the length of a string:
|
|
||||||
|
|
||||||
import String
|
|
||||||
|
|
||||||
stringLength : Decoder Int
|
|
||||||
stringLength =
|
|
||||||
map String.length string
|
|
||||||
|
|
||||||
It is often helpful to use `map` with `oneOf`, like when defining `nullable`:
|
|
||||||
|
|
||||||
nullable : Decoder a -> Decoder (Maybe a)
|
|
||||||
nullable decoder =
|
|
||||||
oneOf
|
|
||||||
[ null Nothing
|
|
||||||
, map Just decoder
|
|
||||||
]
|
|
||||||
-}
|
|
||||||
map : (a -> value) -> Decoder a -> Decoder value
|
|
||||||
map =
|
|
||||||
Native.Json.map1
|
|
||||||
|
|
||||||
|
|
||||||
{-| Try two decoders and then combine the result. We can use this to decode
|
|
||||||
objects with many fields:
|
|
||||||
|
|
||||||
type alias Point = { x : Float, y : Float }
|
|
||||||
|
|
||||||
point : Decoder Point
|
|
||||||
point =
|
|
||||||
map2 Point
|
|
||||||
(field "x" float)
|
|
||||||
(field "y" float)
|
|
||||||
|
|
||||||
-- decodeString point """{ "x": 3, "y": 4 }""" == Ok { x = 3, y = 4 }
|
|
||||||
|
|
||||||
It tries each individual decoder and puts the result together with the `Point`
|
|
||||||
constructor.
|
|
||||||
-}
|
|
||||||
map2 : (a -> b -> value) -> Decoder a -> Decoder b -> Decoder value
|
|
||||||
map2 =
|
|
||||||
Native.Json.map2
|
|
||||||
|
|
||||||
|
|
||||||
{-| Try three decoders and then combine the result. We can use this to decode
|
|
||||||
objects with many fields:
|
|
||||||
|
|
||||||
type alias Person = { name : String, age : Int, height : Float }
|
|
||||||
|
|
||||||
person : Decoder Person
|
|
||||||
person =
|
|
||||||
map3 Person
|
|
||||||
(at ["name"] string)
|
|
||||||
(at ["info","age"] int)
|
|
||||||
(at ["info","height"] float)
|
|
||||||
|
|
||||||
-- json = """{ "name": "tom", "info": { "age": 42, "height": 1.8 } }"""
|
|
||||||
-- decodeString person json == Ok { name = "tom", age = 42, height = 1.8 }
|
|
||||||
|
|
||||||
Like `map2` it tries each decoder in order and then give the results to the
|
|
||||||
`Person` constructor. That can be any function though!
|
|
||||||
-}
|
|
||||||
map3 : (a -> b -> c -> value) -> Decoder a -> Decoder b -> Decoder c -> Decoder value
|
|
||||||
map3 =
|
|
||||||
Native.Json.map3
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map4 : (a -> b -> c -> d -> value) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder value
|
|
||||||
map4 =
|
|
||||||
Native.Json.map4
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map5 : (a -> b -> c -> d -> e -> value) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder value
|
|
||||||
map5 =
|
|
||||||
Native.Json.map5
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map6 : (a -> b -> c -> d -> e -> f -> value) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder f -> Decoder value
|
|
||||||
map6 =
|
|
||||||
Native.Json.map6
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map7 : (a -> b -> c -> d -> e -> f -> g -> value) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder f -> Decoder g -> Decoder value
|
|
||||||
map7 =
|
|
||||||
Native.Json.map7
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map8 : (a -> b -> c -> d -> e -> f -> g -> h -> value) -> Decoder a -> Decoder b -> Decoder c -> Decoder d -> Decoder e -> Decoder f -> Decoder g -> Decoder h -> Decoder value
|
|
||||||
map8 =
|
|
||||||
Native.Json.map8
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- RUN DECODERS
|
|
||||||
|
|
||||||
|
|
||||||
{-| Parse the given string into a JSON value and then run the `Decoder` on it.
|
|
||||||
This will fail if the string is not well-formed JSON or if the `Decoder`
|
|
||||||
fails for some reason.
|
|
||||||
|
|
||||||
decodeString int "4" == Ok 4
|
|
||||||
decodeString int "1 + 2" == Err ...
|
|
||||||
-}
|
|
||||||
decodeString : Decoder a -> String -> Result String a
|
|
||||||
decodeString =
|
|
||||||
Native.Json.runOnString
|
|
||||||
|
|
||||||
|
|
||||||
{-| Run a `Decoder` on some JSON `Value`. You can send these JSON values
|
|
||||||
through ports, so that is probably the main time you would use this function.
|
|
||||||
-}
|
|
||||||
decodeValue : Decoder a -> Value -> Result String a
|
|
||||||
decodeValue =
|
|
||||||
Native.Json.run
|
|
||||||
|
|
||||||
|
|
||||||
{-| A JSON value.
|
|
||||||
-}
|
|
||||||
type alias Value = JsEncode.Value
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- FANCY PRIMITIVES
|
|
||||||
|
|
||||||
|
|
||||||
{-| Ignore the JSON and produce a certain Elm value.
|
|
||||||
|
|
||||||
decodeString (succeed 42) "true" == Ok 42
|
|
||||||
decodeString (succeed 42) "[1,2,3]" == Ok 42
|
|
||||||
decodeString (succeed 42) "hello" == Err ... -- this is not a valid JSON string
|
|
||||||
|
|
||||||
This is handy when used with `oneOf` or `andThen`.
|
|
||||||
-}
|
|
||||||
succeed : a -> Decoder a
|
|
||||||
succeed =
|
|
||||||
Native.Json.succeed
|
|
||||||
|
|
||||||
|
|
||||||
{-| Ignore the JSON and make the decoder fail. This is handy when used with
|
|
||||||
`oneOf` or `andThen` where you want to give a custom error message in some
|
|
||||||
case.
|
|
||||||
|
|
||||||
See the [`andThen`](#andThen) docs for an example.
|
|
||||||
-}
|
|
||||||
fail : String -> Decoder a
|
|
||||||
fail =
|
|
||||||
Native.Json.fail
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create decoders that depend on previous results. If you are creating
|
|
||||||
versioned data, you might do something like this:
|
|
||||||
|
|
||||||
info : Decoder Info
|
|
||||||
info =
|
|
||||||
field "version" int
|
|
||||||
|> andThen infoHelp
|
|
||||||
|
|
||||||
infoHelp : Int -> Decoder Info
|
|
||||||
infoHelp version =
|
|
||||||
case version of
|
|
||||||
4 ->
|
|
||||||
infoDecoder4
|
|
||||||
|
|
||||||
3 ->
|
|
||||||
infoDecoder3
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
fail <|
|
|
||||||
"Trying to decode info, but version "
|
|
||||||
++ toString version ++ " is not supported."
|
|
||||||
|
|
||||||
-- infoDecoder4 : Decoder Info
|
|
||||||
-- infoDecoder3 : Decoder Info
|
|
||||||
-}
|
|
||||||
andThen : (a -> Decoder b) -> Decoder a -> Decoder b
|
|
||||||
andThen =
|
|
||||||
Native.Json.andThen
|
|
||||||
|
|
||||||
|
|
||||||
{-| Sometimes you have JSON with recursive structure, like nested comments.
|
|
||||||
You can use `lazy` to make sure your decoder unrolls lazily.
|
|
||||||
|
|
||||||
type alias Comment =
|
|
||||||
{ message : String
|
|
||||||
, responses : Responses
|
|
||||||
}
|
|
||||||
|
|
||||||
type Responses = Responses (List Comment)
|
|
||||||
|
|
||||||
comment : Decoder Comment
|
|
||||||
comment =
|
|
||||||
map2 Comment
|
|
||||||
(field "message" string)
|
|
||||||
(field "responses" (map Responses (list (lazy (\_ -> comment)))))
|
|
||||||
|
|
||||||
If we had said `list comment` instead, we would start expanding the value
|
|
||||||
infinitely. What is a `comment`? It is a decoder for objects where the
|
|
||||||
`responses` field contains comments. What is a `comment` though? Etc.
|
|
||||||
|
|
||||||
By using `list (lazy (\_ -> comment))` we make sure the decoder only expands
|
|
||||||
to be as deep as the JSON we are given. You can read more about recursive data
|
|
||||||
structures [here][].
|
|
||||||
|
|
||||||
[here]: https://github.com/elm-lang/elm-compiler/blob/master/hints/recursive-alias.md
|
|
||||||
-}
|
|
||||||
lazy : (() -> Decoder a) -> Decoder a
|
|
||||||
lazy thunk =
|
|
||||||
andThen thunk (succeed ())
|
|
||||||
|
|
||||||
|
|
||||||
{-| Do not do anything with a JSON value, just bring it into Elm as a `Value`.
|
|
||||||
This can be useful if you have particularly crazy data that you would like to
|
|
||||||
deal with later. Or if you are going to send it out a port and do not care
|
|
||||||
about its structure.
|
|
||||||
-}
|
|
||||||
value : Decoder Value
|
|
||||||
value =
|
|
||||||
Native.Json.decodePrimitive "value"
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decode a `null` value into some Elm value.
|
|
||||||
|
|
||||||
decodeString (null False) "null" == Ok False
|
|
||||||
decodeString (null 42) "null" == Ok 42
|
|
||||||
decodeString (null 42) "42" == Err ..
|
|
||||||
decodeString (null 42) "false" == Err ..
|
|
||||||
|
|
||||||
So if you ever see a `null`, this will return whatever value you specified.
|
|
||||||
-}
|
|
||||||
null : a -> Decoder a
|
|
||||||
null =
|
|
||||||
Native.Json.decodeNull
|
|
||||||
@@ -1,102 +0,0 @@
|
|||||||
module Json.Encode exposing
|
|
||||||
( Value
|
|
||||||
, encode
|
|
||||||
, string, int, float, bool, null
|
|
||||||
, list, array
|
|
||||||
, object
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Library for turning Elm values into Json values.
|
|
||||||
|
|
||||||
# Encoding
|
|
||||||
@docs encode, Value
|
|
||||||
|
|
||||||
# Primitives
|
|
||||||
@docs string, int, float, bool, null
|
|
||||||
|
|
||||||
# Arrays
|
|
||||||
@docs list, array
|
|
||||||
|
|
||||||
# Objects
|
|
||||||
@docs object
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Array exposing (Array)
|
|
||||||
import Native.Json
|
|
||||||
|
|
||||||
|
|
||||||
{-| Represents a JavaScript value.
|
|
||||||
-}
|
|
||||||
type Value = Value
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a `Value` into a prettified string. The first argument specifies
|
|
||||||
the amount of indentation in the resulting string.
|
|
||||||
|
|
||||||
person =
|
|
||||||
object
|
|
||||||
[ ("name", string "Tom")
|
|
||||||
, ("age", int 42)
|
|
||||||
]
|
|
||||||
|
|
||||||
compact = encode 0 person
|
|
||||||
-- {"name":"Tom","age":42}
|
|
||||||
|
|
||||||
readable = encode 4 person
|
|
||||||
-- {
|
|
||||||
-- "name": "Tom",
|
|
||||||
-- "age": 42
|
|
||||||
-- }
|
|
||||||
-}
|
|
||||||
encode : Int -> Value -> String
|
|
||||||
encode =
|
|
||||||
Native.Json.encode
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
string : String -> Value
|
|
||||||
string =
|
|
||||||
Native.Json.identity
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
int : Int -> Value
|
|
||||||
int =
|
|
||||||
Native.Json.identity
|
|
||||||
|
|
||||||
|
|
||||||
{-| Encode a Float. `Infinity` and `NaN` are encoded as `null`.
|
|
||||||
-}
|
|
||||||
float : Float -> Value
|
|
||||||
float =
|
|
||||||
Native.Json.identity
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
bool : Bool -> Value
|
|
||||||
bool =
|
|
||||||
Native.Json.identity
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
null : Value
|
|
||||||
null =
|
|
||||||
Native.Json.encodeNull
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
object : List (String, Value) -> Value
|
|
||||||
object =
|
|
||||||
Native.Json.encodeObject
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
array : Array Value -> Value
|
|
||||||
array =
|
|
||||||
Native.Json.encodeArray
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
list : List Value -> Value
|
|
||||||
list =
|
|
||||||
Native.Json.encodeList
|
|
||||||
@@ -1,613 +0,0 @@
|
|||||||
module List exposing
|
|
||||||
( isEmpty, length, reverse, member
|
|
||||||
, head, tail, filter, take, drop
|
|
||||||
, singleton, repeat, range, (::), append, concat, intersperse
|
|
||||||
, partition, unzip
|
|
||||||
, map, map2, map3, map4, map5
|
|
||||||
, filterMap, concatMap, indexedMap
|
|
||||||
, foldr, foldl
|
|
||||||
, sum, product, maximum, minimum, all, any, scanl
|
|
||||||
, sort, sortBy, sortWith
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| A library for manipulating lists of values. Every value in a
|
|
||||||
list must have the same type.
|
|
||||||
|
|
||||||
# Basics
|
|
||||||
@docs isEmpty, length, reverse, member
|
|
||||||
|
|
||||||
# Sub-lists
|
|
||||||
@docs head, tail, filter, take, drop
|
|
||||||
|
|
||||||
# Putting Lists Together
|
|
||||||
@docs singleton, repeat, range, (::), append, concat, intersperse
|
|
||||||
|
|
||||||
# Taking Lists Apart
|
|
||||||
@docs partition, unzip
|
|
||||||
|
|
||||||
# Mapping
|
|
||||||
@docs map, map2, map3, map4, map5
|
|
||||||
|
|
||||||
If you can think of a legitimate use of `mapN` where `N` is 6 or more, please
|
|
||||||
let us know on [the list](https://groups.google.com/forum/#!forum/elm-discuss).
|
|
||||||
The current sentiment is that it is already quite error prone once you get to
|
|
||||||
4 and possibly should be approached another way.
|
|
||||||
|
|
||||||
# Special Maps
|
|
||||||
@docs filterMap, concatMap, indexedMap
|
|
||||||
|
|
||||||
# Folding
|
|
||||||
@docs foldr, foldl
|
|
||||||
|
|
||||||
# Special Folds
|
|
||||||
@docs sum, product, maximum, minimum, all, any, scanl
|
|
||||||
|
|
||||||
# Sorting
|
|
||||||
@docs sort, sortBy, sortWith
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Maybe
|
|
||||||
import Maybe exposing ( Maybe(Just,Nothing) )
|
|
||||||
import Native.List
|
|
||||||
|
|
||||||
|
|
||||||
{-| Add an element to the front of a list. Pronounced *cons*.
|
|
||||||
|
|
||||||
1 :: [2,3] == [1,2,3]
|
|
||||||
1 :: [] == [1]
|
|
||||||
-}
|
|
||||||
(::) : a -> List a -> List a
|
|
||||||
(::) =
|
|
||||||
Native.List.cons
|
|
||||||
|
|
||||||
|
|
||||||
infixr 5 ::
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the first element of a list.
|
|
||||||
|
|
||||||
head [1,2,3] == Just 1
|
|
||||||
head [] == Nothing
|
|
||||||
-}
|
|
||||||
head : List a -> Maybe a
|
|
||||||
head list =
|
|
||||||
case list of
|
|
||||||
x :: xs ->
|
|
||||||
Just x
|
|
||||||
|
|
||||||
[] ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the rest of the list.
|
|
||||||
|
|
||||||
tail [1,2,3] == Just [2,3]
|
|
||||||
tail [] == Nothing
|
|
||||||
-}
|
|
||||||
tail : List a -> Maybe (List a)
|
|
||||||
tail list =
|
|
||||||
case list of
|
|
||||||
x :: xs ->
|
|
||||||
Just xs
|
|
||||||
|
|
||||||
[] ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if a list is empty.
|
|
||||||
|
|
||||||
isEmpty [] == True
|
|
||||||
-}
|
|
||||||
isEmpty : List a -> Bool
|
|
||||||
isEmpty xs =
|
|
||||||
case xs of
|
|
||||||
[] ->
|
|
||||||
True
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
False
|
|
||||||
|
|
||||||
|
|
||||||
{-| Figure out whether a list contains a value.
|
|
||||||
|
|
||||||
member 9 [1,2,3,4] == False
|
|
||||||
member 4 [1,2,3,4] == True
|
|
||||||
-}
|
|
||||||
member : a -> List a -> Bool
|
|
||||||
member x xs =
|
|
||||||
any (\a -> a == x) xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Apply a function to every element of a list.
|
|
||||||
|
|
||||||
map sqrt [1,4,9] == [1,2,3]
|
|
||||||
|
|
||||||
map not [True,False,True] == [False,True,False]
|
|
||||||
-}
|
|
||||||
map : (a -> b) -> List a -> List b
|
|
||||||
map f xs =
|
|
||||||
foldr (\x acc -> f x :: acc) [] xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Same as `map` but the function is also applied to the index of each
|
|
||||||
element (starting at zero).
|
|
||||||
|
|
||||||
indexedMap (,) ["Tom","Sue","Bob"] == [ (0,"Tom"), (1,"Sue"), (2,"Bob") ]
|
|
||||||
-}
|
|
||||||
indexedMap : (Int -> a -> b) -> List a -> List b
|
|
||||||
indexedMap f xs =
|
|
||||||
map2 f (range 0 (length xs - 1)) xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reduce a list from the left.
|
|
||||||
|
|
||||||
foldl (::) [] [1,2,3] == [3,2,1]
|
|
||||||
-}
|
|
||||||
foldl : (a -> b -> b) -> b -> List a -> b
|
|
||||||
foldl func acc list =
|
|
||||||
case list of
|
|
||||||
[] ->
|
|
||||||
acc
|
|
||||||
|
|
||||||
x :: xs ->
|
|
||||||
foldl func (func x acc) xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reduce a list from the right.
|
|
||||||
|
|
||||||
foldr (+) 0 [1,2,3] == 6
|
|
||||||
-}
|
|
||||||
foldr : (a -> b -> b) -> b -> List a -> b
|
|
||||||
foldr =
|
|
||||||
Native.List.foldr
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reduce a list from the left, building up all of the intermediate results into a list.
|
|
||||||
|
|
||||||
scanl (+) 0 [1,2,3,4] == [0,1,3,6,10]
|
|
||||||
-}
|
|
||||||
scanl : (a -> b -> b) -> b -> List a -> List b
|
|
||||||
scanl f b xs =
|
|
||||||
let
|
|
||||||
scan1 x accAcc =
|
|
||||||
case accAcc of
|
|
||||||
acc :: _ ->
|
|
||||||
f x acc :: accAcc
|
|
||||||
|
|
||||||
[] ->
|
|
||||||
[] -- impossible
|
|
||||||
in
|
|
||||||
reverse (foldl scan1 [b] xs)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Keep only elements that satisfy the predicate.
|
|
||||||
|
|
||||||
filter isEven [1,2,3,4,5,6] == [2,4,6]
|
|
||||||
-}
|
|
||||||
filter : (a -> Bool) -> List a -> List a
|
|
||||||
filter pred xs =
|
|
||||||
let
|
|
||||||
conditionalCons front back =
|
|
||||||
if pred front then
|
|
||||||
front :: back
|
|
||||||
|
|
||||||
else
|
|
||||||
back
|
|
||||||
in
|
|
||||||
foldr conditionalCons [] xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Apply a function that may succeed to all values in the list, but only keep
|
|
||||||
the successes.
|
|
||||||
|
|
||||||
onlyTeens =
|
|
||||||
filterMap isTeen [3, 15, 12, 18, 24] == [15, 18]
|
|
||||||
|
|
||||||
isTeen : Int -> Maybe Int
|
|
||||||
isTeen n =
|
|
||||||
if 13 <= n && n <= 19 then
|
|
||||||
Just n
|
|
||||||
|
|
||||||
else
|
|
||||||
Nothing
|
|
||||||
-}
|
|
||||||
filterMap : (a -> Maybe b) -> List a -> List b
|
|
||||||
filterMap f xs =
|
|
||||||
foldr (maybeCons f) [] xs
|
|
||||||
|
|
||||||
|
|
||||||
maybeCons : (a -> Maybe b) -> a -> List b -> List b
|
|
||||||
maybeCons f mx xs =
|
|
||||||
case f mx of
|
|
||||||
Just x ->
|
|
||||||
x :: xs
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine the length of a list.
|
|
||||||
|
|
||||||
length [1,2,3] == 3
|
|
||||||
-}
|
|
||||||
length : List a -> Int
|
|
||||||
length xs =
|
|
||||||
foldl (\_ i -> i + 1) 0 xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reverse a list.
|
|
||||||
|
|
||||||
reverse [1,2,3,4] == [4,3,2,1]
|
|
||||||
-}
|
|
||||||
reverse : List a -> List a
|
|
||||||
reverse list =
|
|
||||||
foldl (::) [] list
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if all elements satisfy the predicate.
|
|
||||||
|
|
||||||
all isEven [2,4] == True
|
|
||||||
all isEven [2,3] == False
|
|
||||||
all isEven [] == True
|
|
||||||
-}
|
|
||||||
all : (a -> Bool) -> List a -> Bool
|
|
||||||
all isOkay list =
|
|
||||||
not (any (not << isOkay) list)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if any elements satisfy the predicate.
|
|
||||||
|
|
||||||
any isEven [2,3] == True
|
|
||||||
any isEven [1,3] == False
|
|
||||||
any isEven [] == False
|
|
||||||
-}
|
|
||||||
any : (a -> Bool) -> List a -> Bool
|
|
||||||
any isOkay list =
|
|
||||||
case list of
|
|
||||||
[] ->
|
|
||||||
False
|
|
||||||
|
|
||||||
x :: xs ->
|
|
||||||
-- note: (isOkay x || any isOkay xs) would not get TCO
|
|
||||||
if isOkay x then
|
|
||||||
True
|
|
||||||
|
|
||||||
else
|
|
||||||
any isOkay xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Put two lists together.
|
|
||||||
|
|
||||||
append [1,1,2] [3,5,8] == [1,1,2,3,5,8]
|
|
||||||
append ['a','b'] ['c'] == ['a','b','c']
|
|
||||||
|
|
||||||
You can also use [the `(++)` operator](Basics#++) to append lists.
|
|
||||||
-}
|
|
||||||
append : List a -> List a -> List a
|
|
||||||
append xs ys =
|
|
||||||
case ys of
|
|
||||||
[] ->
|
|
||||||
xs
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
foldr (::) ys xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Concatenate a bunch of lists into a single list:
|
|
||||||
|
|
||||||
concat [[1,2],[3],[4,5]] == [1,2,3,4,5]
|
|
||||||
-}
|
|
||||||
concat : List (List a) -> List a
|
|
||||||
concat lists =
|
|
||||||
foldr append [] lists
|
|
||||||
|
|
||||||
|
|
||||||
{-| Map a given function onto a list and flatten the resulting lists.
|
|
||||||
|
|
||||||
concatMap f xs == concat (map f xs)
|
|
||||||
-}
|
|
||||||
concatMap : (a -> List b) -> List a -> List b
|
|
||||||
concatMap f list =
|
|
||||||
concat (map f list)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the sum of the list elements.
|
|
||||||
|
|
||||||
sum [1,2,3,4] == 10
|
|
||||||
-}
|
|
||||||
sum : List number -> number
|
|
||||||
sum numbers =
|
|
||||||
foldl (+) 0 numbers
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the product of the list elements.
|
|
||||||
|
|
||||||
product [1,2,3,4] == 24
|
|
||||||
-}
|
|
||||||
product : List number -> number
|
|
||||||
product numbers =
|
|
||||||
foldl (*) 1 numbers
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the maximum element in a non-empty list.
|
|
||||||
|
|
||||||
maximum [1,4,2] == Just 4
|
|
||||||
maximum [] == Nothing
|
|
||||||
-}
|
|
||||||
maximum : List comparable -> Maybe comparable
|
|
||||||
maximum list =
|
|
||||||
case list of
|
|
||||||
x :: xs ->
|
|
||||||
Just (foldl max x xs)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the minimum element in a non-empty list.
|
|
||||||
|
|
||||||
minimum [3,2,1] == Just 1
|
|
||||||
minimum [] == Nothing
|
|
||||||
-}
|
|
||||||
minimum : List comparable -> Maybe comparable
|
|
||||||
minimum list =
|
|
||||||
case list of
|
|
||||||
x :: xs ->
|
|
||||||
Just (foldl min x xs)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Partition a list based on a predicate. The first list contains all values
|
|
||||||
that satisfy the predicate, and the second list contains all the value that do
|
|
||||||
not.
|
|
||||||
|
|
||||||
partition (\x -> x < 3) [0,1,2,3,4,5] == ([0,1,2], [3,4,5])
|
|
||||||
partition isEven [0,1,2,3,4,5] == ([0,2,4], [1,3,5])
|
|
||||||
-}
|
|
||||||
partition : (a -> Bool) -> List a -> (List a, List a)
|
|
||||||
partition pred list =
|
|
||||||
let
|
|
||||||
step x (trues, falses) =
|
|
||||||
if pred x then
|
|
||||||
(x :: trues, falses)
|
|
||||||
|
|
||||||
else
|
|
||||||
(trues, x :: falses)
|
|
||||||
in
|
|
||||||
foldr step ([],[]) list
|
|
||||||
|
|
||||||
|
|
||||||
{-| Combine two lists, combining them with the given function.
|
|
||||||
If one list is longer, the extra elements are dropped.
|
|
||||||
|
|
||||||
map2 (+) [1,2,3] [1,2,3,4] == [2,4,6]
|
|
||||||
|
|
||||||
map2 (,) [1,2,3] ['a','b'] == [ (1,'a'), (2,'b') ]
|
|
||||||
|
|
||||||
pairs : List a -> List b -> List (a,b)
|
|
||||||
pairs lefts rights =
|
|
||||||
map2 (,) lefts rights
|
|
||||||
-}
|
|
||||||
map2 : (a -> b -> result) -> List a -> List b -> List result
|
|
||||||
map2 =
|
|
||||||
Native.List.map2
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map3 : (a -> b -> c -> result) -> List a -> List b -> List c -> List result
|
|
||||||
map3 =
|
|
||||||
Native.List.map3
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map4 : (a -> b -> c -> d -> result) -> List a -> List b -> List c -> List d -> List result
|
|
||||||
map4 =
|
|
||||||
Native.List.map4
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map5 : (a -> b -> c -> d -> e -> result) -> List a -> List b -> List c -> List d -> List e -> List result
|
|
||||||
map5 =
|
|
||||||
Native.List.map5
|
|
||||||
|
|
||||||
|
|
||||||
{-| Decompose a list of tuples into a tuple of lists.
|
|
||||||
|
|
||||||
unzip [(0, True), (17, False), (1337, True)] == ([0,17,1337], [True,False,True])
|
|
||||||
-}
|
|
||||||
unzip : List (a,b) -> (List a, List b)
|
|
||||||
unzip pairs =
|
|
||||||
let
|
|
||||||
step (x,y) (xs,ys) =
|
|
||||||
(x :: xs, y :: ys)
|
|
||||||
in
|
|
||||||
foldr step ([], []) pairs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Places the given value between all members of the given list.
|
|
||||||
|
|
||||||
intersperse "on" ["turtles","turtles","turtles"] == ["turtles","on","turtles","on","turtles"]
|
|
||||||
-}
|
|
||||||
intersperse : a -> List a -> List a
|
|
||||||
intersperse sep xs =
|
|
||||||
case xs of
|
|
||||||
[] ->
|
|
||||||
[]
|
|
||||||
|
|
||||||
hd :: tl ->
|
|
||||||
let
|
|
||||||
step x rest =
|
|
||||||
sep :: x :: rest
|
|
||||||
|
|
||||||
spersed =
|
|
||||||
foldr step [] tl
|
|
||||||
in
|
|
||||||
hd :: spersed
|
|
||||||
|
|
||||||
|
|
||||||
{-| Take the first *n* members of a list.
|
|
||||||
|
|
||||||
take 2 [1,2,3,4] == [1,2]
|
|
||||||
-}
|
|
||||||
take : Int -> List a -> List a
|
|
||||||
take n list =
|
|
||||||
takeFast 0 n list
|
|
||||||
|
|
||||||
|
|
||||||
takeFast : Int -> Int -> List a -> List a
|
|
||||||
takeFast ctr n list =
|
|
||||||
if n <= 0 then
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
case ( n, list ) of
|
|
||||||
( _, [] ) ->
|
|
||||||
list
|
|
||||||
|
|
||||||
( 1, x :: _ ) ->
|
|
||||||
[ x ]
|
|
||||||
|
|
||||||
( 2, x :: y :: _ ) ->
|
|
||||||
[ x, y ]
|
|
||||||
|
|
||||||
( 3, x :: y :: z :: _ ) ->
|
|
||||||
[ x, y, z ]
|
|
||||||
|
|
||||||
( _, x :: y :: z :: w :: tl ) ->
|
|
||||||
if ctr > 1000 then
|
|
||||||
x :: y :: z :: w :: takeTailRec (n - 4) tl
|
|
||||||
else
|
|
||||||
x :: y :: z :: w :: takeFast (ctr + 1) (n - 4) tl
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
list
|
|
||||||
|
|
||||||
takeTailRec : Int -> List a -> List a
|
|
||||||
takeTailRec n list =
|
|
||||||
reverse (takeReverse n list [])
|
|
||||||
|
|
||||||
|
|
||||||
takeReverse : Int -> List a -> List a -> List a
|
|
||||||
takeReverse n list taken =
|
|
||||||
if n <= 0 then
|
|
||||||
taken
|
|
||||||
else
|
|
||||||
case list of
|
|
||||||
[] ->
|
|
||||||
taken
|
|
||||||
|
|
||||||
x :: xs ->
|
|
||||||
takeReverse (n - 1) xs (x :: taken)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Drop the first *n* members of a list.
|
|
||||||
|
|
||||||
drop 2 [1,2,3,4] == [3,4]
|
|
||||||
-}
|
|
||||||
drop : Int -> List a -> List a
|
|
||||||
drop n list =
|
|
||||||
if n <= 0 then
|
|
||||||
list
|
|
||||||
|
|
||||||
else
|
|
||||||
case list of
|
|
||||||
[] ->
|
|
||||||
list
|
|
||||||
|
|
||||||
x :: xs ->
|
|
||||||
drop (n-1) xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a list with only one element:
|
|
||||||
|
|
||||||
singleton 1234 == [1234]
|
|
||||||
singleton "hi" == ["hi"]
|
|
||||||
-}
|
|
||||||
singleton : a -> List a
|
|
||||||
singleton value =
|
|
||||||
[value]
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a list with *n* copies of a value:
|
|
||||||
|
|
||||||
repeat 3 (0,0) == [(0,0),(0,0),(0,0)]
|
|
||||||
-}
|
|
||||||
repeat : Int -> a -> List a
|
|
||||||
repeat n value =
|
|
||||||
repeatHelp [] n value
|
|
||||||
|
|
||||||
|
|
||||||
repeatHelp : List a -> Int -> a -> List a
|
|
||||||
repeatHelp result n value =
|
|
||||||
if n <= 0 then
|
|
||||||
result
|
|
||||||
|
|
||||||
else
|
|
||||||
repeatHelp (value :: result) (n-1) value
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a list of numbers, every element increasing by one.
|
|
||||||
You give the lowest and highest number that should be in the list.
|
|
||||||
|
|
||||||
range 3 6 == [3, 4, 5, 6]
|
|
||||||
range 3 3 == [3]
|
|
||||||
range 6 3 == []
|
|
||||||
-}
|
|
||||||
range : Int -> Int -> List Int
|
|
||||||
range lo hi =
|
|
||||||
rangeHelp lo hi []
|
|
||||||
|
|
||||||
|
|
||||||
rangeHelp : Int -> Int -> List Int -> List Int
|
|
||||||
rangeHelp lo hi list =
|
|
||||||
if lo <= hi then
|
|
||||||
rangeHelp lo (hi - 1) (hi :: list)
|
|
||||||
|
|
||||||
else
|
|
||||||
list
|
|
||||||
|
|
||||||
|
|
||||||
{-| Sort values from lowest to highest
|
|
||||||
|
|
||||||
sort [3,1,5] == [1,3,5]
|
|
||||||
-}
|
|
||||||
sort : List comparable -> List comparable
|
|
||||||
sort xs =
|
|
||||||
sortBy identity xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Sort values by a derived property.
|
|
||||||
|
|
||||||
alice = { name="Alice", height=1.62 }
|
|
||||||
bob = { name="Bob" , height=1.85 }
|
|
||||||
chuck = { name="Chuck", height=1.76 }
|
|
||||||
|
|
||||||
sortBy .name [chuck,alice,bob] == [alice,bob,chuck]
|
|
||||||
sortBy .height [chuck,alice,bob] == [alice,chuck,bob]
|
|
||||||
|
|
||||||
sortBy String.length ["mouse","cat"] == ["cat","mouse"]
|
|
||||||
-}
|
|
||||||
sortBy : (a -> comparable) -> List a -> List a
|
|
||||||
sortBy =
|
|
||||||
Native.List.sortBy
|
|
||||||
|
|
||||||
|
|
||||||
{-| Sort values with a custom comparison function.
|
|
||||||
|
|
||||||
sortWith flippedComparison [1,2,3,4,5] == [5,4,3,2,1]
|
|
||||||
|
|
||||||
flippedComparison a b =
|
|
||||||
case compare a b of
|
|
||||||
LT -> GT
|
|
||||||
EQ -> EQ
|
|
||||||
GT -> LT
|
|
||||||
|
|
||||||
This is also the most general sort function, allowing you
|
|
||||||
to define any other: `sort == sortWith compare`
|
|
||||||
-}
|
|
||||||
sortWith : (a -> a -> Order) -> List a -> List a
|
|
||||||
sortWith =
|
|
||||||
Native.List.sortWith
|
|
||||||
@@ -1,157 +0,0 @@
|
|||||||
module Maybe exposing
|
|
||||||
( Maybe(Just,Nothing)
|
|
||||||
, andThen
|
|
||||||
, map, map2, map3, map4, map5
|
|
||||||
, withDefault
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| This library fills a bunch of important niches in Elm. A `Maybe` can help
|
|
||||||
you with optional arguments, error handling, and records with optional fields.
|
|
||||||
|
|
||||||
# Definition
|
|
||||||
@docs Maybe
|
|
||||||
|
|
||||||
# Common Helpers
|
|
||||||
@docs withDefault, map, map2, map3, map4, map5
|
|
||||||
|
|
||||||
# Chaining Maybes
|
|
||||||
@docs andThen
|
|
||||||
-}
|
|
||||||
|
|
||||||
{-| Represent values that may or may not exist. It can be useful if you have a
|
|
||||||
record field that is only filled in sometimes. Or if a function takes a value
|
|
||||||
sometimes, but does not absolutely need it.
|
|
||||||
|
|
||||||
-- A person, but maybe we do not know their age.
|
|
||||||
type alias Person =
|
|
||||||
{ name : String
|
|
||||||
, age : Maybe Int
|
|
||||||
}
|
|
||||||
|
|
||||||
tom = { name = "Tom", age = Just 42 }
|
|
||||||
sue = { name = "Sue", age = Nothing }
|
|
||||||
-}
|
|
||||||
type Maybe a
|
|
||||||
= Just a
|
|
||||||
| Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Provide a default value, turning an optional value into a normal
|
|
||||||
value. This comes in handy when paired with functions like
|
|
||||||
[`Dict.get`](Dict#get) which gives back a `Maybe`.
|
|
||||||
|
|
||||||
withDefault 100 (Just 42) -- 42
|
|
||||||
withDefault 100 Nothing -- 100
|
|
||||||
|
|
||||||
withDefault "unknown" (Dict.get "Tom" Dict.empty) -- "unknown"
|
|
||||||
|
|
||||||
-}
|
|
||||||
withDefault : a -> Maybe a -> a
|
|
||||||
withDefault default maybe =
|
|
||||||
case maybe of
|
|
||||||
Just value -> value
|
|
||||||
Nothing -> default
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform a `Maybe` value with a given function:
|
|
||||||
|
|
||||||
map sqrt (Just 9) == Just 3
|
|
||||||
map sqrt Nothing == Nothing
|
|
||||||
-}
|
|
||||||
map : (a -> b) -> Maybe a -> Maybe b
|
|
||||||
map f maybe =
|
|
||||||
case maybe of
|
|
||||||
Just value -> Just (f value)
|
|
||||||
Nothing -> Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Apply a function if all the arguments are `Just` a value.
|
|
||||||
|
|
||||||
map2 (+) (Just 3) (Just 4) == Just 7
|
|
||||||
map2 (+) (Just 3) Nothing == Nothing
|
|
||||||
map2 (+) Nothing (Just 4) == Nothing
|
|
||||||
-}
|
|
||||||
map2 : (a -> b -> value) -> Maybe a -> Maybe b -> Maybe value
|
|
||||||
map2 func ma mb =
|
|
||||||
case (ma,mb) of
|
|
||||||
(Just a, Just b) ->
|
|
||||||
Just (func a b)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map3 : (a -> b -> c -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe value
|
|
||||||
map3 func ma mb mc =
|
|
||||||
case (ma,mb,mc) of
|
|
||||||
(Just a, Just b, Just c) ->
|
|
||||||
Just (func a b c)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map4 : (a -> b -> c -> d -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe value
|
|
||||||
map4 func ma mb mc md =
|
|
||||||
case (ma,mb,mc,md) of
|
|
||||||
(Just a, Just b, Just c, Just d) ->
|
|
||||||
Just (func a b c d)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map5 : (a -> b -> c -> d -> e -> value) -> Maybe a -> Maybe b -> Maybe c -> Maybe d -> Maybe e -> Maybe value
|
|
||||||
map5 func ma mb mc md me =
|
|
||||||
case (ma,mb,mc,md,me) of
|
|
||||||
(Just a, Just b, Just c, Just d, Just e) ->
|
|
||||||
Just (func a b c d e)
|
|
||||||
|
|
||||||
_ ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Chain together many computations that may fail. It is helpful to see its
|
|
||||||
definition:
|
|
||||||
|
|
||||||
andThen : (a -> Maybe b) -> Maybe a -> Maybe b
|
|
||||||
andThen callback maybe =
|
|
||||||
case maybe of
|
|
||||||
Just value ->
|
|
||||||
callback value
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
This means we only continue with the callback if things are going well. For
|
|
||||||
example, say you need to use (`head : List Int -> Maybe Int`) to get the
|
|
||||||
first month from a `List` and then make sure it is between 1 and 12:
|
|
||||||
|
|
||||||
toValidMonth : Int -> Maybe Int
|
|
||||||
toValidMonth month =
|
|
||||||
if month >= 1 && month <= 12 then
|
|
||||||
Just month
|
|
||||||
else
|
|
||||||
Nothing
|
|
||||||
|
|
||||||
getFirstMonth : List Int -> Maybe Int
|
|
||||||
getFirstMonth months =
|
|
||||||
head months
|
|
||||||
|> andThen toValidMonth
|
|
||||||
|
|
||||||
If `head` fails and results in `Nothing` (because the `List` was `empty`),
|
|
||||||
this entire chain of operations will short-circuit and result in `Nothing`.
|
|
||||||
If `toValidMonth` results in `Nothing`, again the chain of computations
|
|
||||||
will result in `Nothing`.
|
|
||||||
-}
|
|
||||||
andThen : (a -> Maybe b) -> Maybe a -> Maybe b
|
|
||||||
andThen callback maybeValue =
|
|
||||||
case maybeValue of
|
|
||||||
Just value ->
|
|
||||||
callback value
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
Nothing
|
|
||||||
@@ -1,967 +0,0 @@
|
|||||||
//import Native.List //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Array = function() {
|
|
||||||
|
|
||||||
// A RRB-Tree has two distinct data types.
|
|
||||||
// Leaf -> "height" is always 0
|
|
||||||
// "table" is an array of elements
|
|
||||||
// Node -> "height" is always greater than 0
|
|
||||||
// "table" is an array of child nodes
|
|
||||||
// "lengths" is an array of accumulated lengths of the child nodes
|
|
||||||
|
|
||||||
// M is the maximal table size. 32 seems fast. E is the allowed increase
|
|
||||||
// of search steps when concatting to find an index. Lower values will
|
|
||||||
// decrease balancing, but will increase search steps.
|
|
||||||
var M = 32;
|
|
||||||
var E = 2;
|
|
||||||
|
|
||||||
// An empty array.
|
|
||||||
var empty = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: 0,
|
|
||||||
table: []
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
function get(i, array)
|
|
||||||
{
|
|
||||||
if (i < 0 || i >= length(array))
|
|
||||||
{
|
|
||||||
throw new Error(
|
|
||||||
'Index ' + i + ' is out of range. Check the length of ' +
|
|
||||||
'your array first or use getMaybe or getWithDefault.');
|
|
||||||
}
|
|
||||||
return unsafeGet(i, array);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function unsafeGet(i, array)
|
|
||||||
{
|
|
||||||
for (var x = array.height; x > 0; x--)
|
|
||||||
{
|
|
||||||
var slot = i >> (x * 5);
|
|
||||||
while (array.lengths[slot] <= i)
|
|
||||||
{
|
|
||||||
slot++;
|
|
||||||
}
|
|
||||||
if (slot > 0)
|
|
||||||
{
|
|
||||||
i -= array.lengths[slot - 1];
|
|
||||||
}
|
|
||||||
array = array.table[slot];
|
|
||||||
}
|
|
||||||
return array.table[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Sets the value at the index i. Only the nodes leading to i will get
|
|
||||||
// copied and updated.
|
|
||||||
function set(i, item, array)
|
|
||||||
{
|
|
||||||
if (i < 0 || length(array) <= i)
|
|
||||||
{
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
return unsafeSet(i, item, array);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function unsafeSet(i, item, array)
|
|
||||||
{
|
|
||||||
array = nodeCopy(array);
|
|
||||||
|
|
||||||
if (array.height === 0)
|
|
||||||
{
|
|
||||||
array.table[i] = item;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var slot = getSlot(i, array);
|
|
||||||
if (slot > 0)
|
|
||||||
{
|
|
||||||
i -= array.lengths[slot - 1];
|
|
||||||
}
|
|
||||||
array.table[slot] = unsafeSet(i, item, array.table[slot]);
|
|
||||||
}
|
|
||||||
return array;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function initialize(len, f)
|
|
||||||
{
|
|
||||||
if (len <= 0)
|
|
||||||
{
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
var h = Math.floor( Math.log(len) / Math.log(M) );
|
|
||||||
return initialize_(f, h, 0, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
function initialize_(f, h, from, to)
|
|
||||||
{
|
|
||||||
if (h === 0)
|
|
||||||
{
|
|
||||||
var table = new Array((to - from) % (M + 1));
|
|
||||||
for (var i = 0; i < table.length; i++)
|
|
||||||
{
|
|
||||||
table[i] = f(from + i);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: 0,
|
|
||||||
table: table
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var step = Math.pow(M, h);
|
|
||||||
var table = new Array(Math.ceil((to - from) / step));
|
|
||||||
var lengths = new Array(table.length);
|
|
||||||
for (var i = 0; i < table.length; i++)
|
|
||||||
{
|
|
||||||
table[i] = initialize_(f, h - 1, from + (i * step), Math.min(from + ((i + 1) * step), to));
|
|
||||||
lengths[i] = length(table[i]) + (i > 0 ? lengths[i-1] : 0);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: h,
|
|
||||||
table: table,
|
|
||||||
lengths: lengths
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromList(list)
|
|
||||||
{
|
|
||||||
if (list.ctor === '[]')
|
|
||||||
{
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allocate M sized blocks (table) and write list elements to it.
|
|
||||||
var table = new Array(M);
|
|
||||||
var nodes = [];
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
while (list.ctor !== '[]')
|
|
||||||
{
|
|
||||||
table[i] = list._0;
|
|
||||||
list = list._1;
|
|
||||||
i++;
|
|
||||||
|
|
||||||
// table is full, so we can push a leaf containing it into the
|
|
||||||
// next node.
|
|
||||||
if (i === M)
|
|
||||||
{
|
|
||||||
var leaf = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: 0,
|
|
||||||
table: table
|
|
||||||
};
|
|
||||||
fromListPush(leaf, nodes);
|
|
||||||
table = new Array(M);
|
|
||||||
i = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maybe there is something left on the table.
|
|
||||||
if (i > 0)
|
|
||||||
{
|
|
||||||
var leaf = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: 0,
|
|
||||||
table: table.splice(0, i)
|
|
||||||
};
|
|
||||||
fromListPush(leaf, nodes);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go through all of the nodes and eventually push them into higher nodes.
|
|
||||||
for (var h = 0; h < nodes.length - 1; h++)
|
|
||||||
{
|
|
||||||
if (nodes[h].table.length > 0)
|
|
||||||
{
|
|
||||||
fromListPush(nodes[h], nodes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var head = nodes[nodes.length - 1];
|
|
||||||
if (head.height > 0 && head.table.length === 1)
|
|
||||||
{
|
|
||||||
return head.table[0];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return head;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Push a node into a higher node as a child.
|
|
||||||
function fromListPush(toPush, nodes)
|
|
||||||
{
|
|
||||||
var h = toPush.height;
|
|
||||||
|
|
||||||
// Maybe the node on this height does not exist.
|
|
||||||
if (nodes.length === h)
|
|
||||||
{
|
|
||||||
var node = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: h + 1,
|
|
||||||
table: [],
|
|
||||||
lengths: []
|
|
||||||
};
|
|
||||||
nodes.push(node);
|
|
||||||
}
|
|
||||||
|
|
||||||
nodes[h].table.push(toPush);
|
|
||||||
var len = length(toPush);
|
|
||||||
if (nodes[h].lengths.length > 0)
|
|
||||||
{
|
|
||||||
len += nodes[h].lengths[nodes[h].lengths.length - 1];
|
|
||||||
}
|
|
||||||
nodes[h].lengths.push(len);
|
|
||||||
|
|
||||||
if (nodes[h].table.length === M)
|
|
||||||
{
|
|
||||||
fromListPush(nodes[h], nodes);
|
|
||||||
nodes[h] = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: h + 1,
|
|
||||||
table: [],
|
|
||||||
lengths: []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pushes an item via push_ to the bottom right of a tree.
|
|
||||||
function push(item, a)
|
|
||||||
{
|
|
||||||
var pushed = push_(item, a);
|
|
||||||
if (pushed !== null)
|
|
||||||
{
|
|
||||||
return pushed;
|
|
||||||
}
|
|
||||||
|
|
||||||
var newTree = create(item, a.height);
|
|
||||||
return siblise(a, newTree);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively tries to push an item to the bottom-right most
|
|
||||||
// tree possible. If there is no space left for the item,
|
|
||||||
// null will be returned.
|
|
||||||
function push_(item, a)
|
|
||||||
{
|
|
||||||
// Handle resursion stop at leaf level.
|
|
||||||
if (a.height === 0)
|
|
||||||
{
|
|
||||||
if (a.table.length < M)
|
|
||||||
{
|
|
||||||
var newA = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: 0,
|
|
||||||
table: a.table.slice()
|
|
||||||
};
|
|
||||||
newA.table.push(item);
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively push
|
|
||||||
var pushed = push_(item, botRight(a));
|
|
||||||
|
|
||||||
// There was space in the bottom right tree, so the slot will
|
|
||||||
// be updated.
|
|
||||||
if (pushed !== null)
|
|
||||||
{
|
|
||||||
var newA = nodeCopy(a);
|
|
||||||
newA.table[newA.table.length - 1] = pushed;
|
|
||||||
newA.lengths[newA.lengths.length - 1]++;
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
|
|
||||||
// When there was no space left, check if there is space left
|
|
||||||
// for a new slot with a tree which contains only the item
|
|
||||||
// at the bottom.
|
|
||||||
if (a.table.length < M)
|
|
||||||
{
|
|
||||||
var newSlot = create(item, a.height - 1);
|
|
||||||
var newA = nodeCopy(a);
|
|
||||||
newA.table.push(newSlot);
|
|
||||||
newA.lengths.push(newA.lengths[newA.lengths.length - 1] + length(newSlot));
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Converts an array into a list of elements.
|
|
||||||
function toList(a)
|
|
||||||
{
|
|
||||||
return toList_(_elm_lang$core$Native_List.Nil, a);
|
|
||||||
}
|
|
||||||
|
|
||||||
function toList_(list, a)
|
|
||||||
{
|
|
||||||
for (var i = a.table.length - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
list =
|
|
||||||
a.height === 0
|
|
||||||
? _elm_lang$core$Native_List.Cons(a.table[i], list)
|
|
||||||
: toList_(list, a.table[i]);
|
|
||||||
}
|
|
||||||
return list;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maps a function over the elements of an array.
|
|
||||||
function map(f, a)
|
|
||||||
{
|
|
||||||
var newA = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: a.height,
|
|
||||||
table: new Array(a.table.length)
|
|
||||||
};
|
|
||||||
if (a.height > 0)
|
|
||||||
{
|
|
||||||
newA.lengths = a.lengths;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < a.table.length; i++)
|
|
||||||
{
|
|
||||||
newA.table[i] =
|
|
||||||
a.height === 0
|
|
||||||
? f(a.table[i])
|
|
||||||
: map(f, a.table[i]);
|
|
||||||
}
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Maps a function over the elements with their index as first argument.
|
|
||||||
function indexedMap(f, a)
|
|
||||||
{
|
|
||||||
return indexedMap_(f, a, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
function indexedMap_(f, a, from)
|
|
||||||
{
|
|
||||||
var newA = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: a.height,
|
|
||||||
table: new Array(a.table.length)
|
|
||||||
};
|
|
||||||
if (a.height > 0)
|
|
||||||
{
|
|
||||||
newA.lengths = a.lengths;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < a.table.length; i++)
|
|
||||||
{
|
|
||||||
newA.table[i] =
|
|
||||||
a.height === 0
|
|
||||||
? A2(f, from + i, a.table[i])
|
|
||||||
: indexedMap_(f, a.table[i], i == 0 ? from : from + a.lengths[i - 1]);
|
|
||||||
}
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
|
|
||||||
function foldl(f, b, a)
|
|
||||||
{
|
|
||||||
if (a.height === 0)
|
|
||||||
{
|
|
||||||
for (var i = 0; i < a.table.length; i++)
|
|
||||||
{
|
|
||||||
b = A2(f, a.table[i], b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (var i = 0; i < a.table.length; i++)
|
|
||||||
{
|
|
||||||
b = foldl(f, b, a.table[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
function foldr(f, b, a)
|
|
||||||
{
|
|
||||||
if (a.height === 0)
|
|
||||||
{
|
|
||||||
for (var i = a.table.length; i--; )
|
|
||||||
{
|
|
||||||
b = A2(f, a.table[i], b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
for (var i = a.table.length; i--; )
|
|
||||||
{
|
|
||||||
b = foldr(f, b, a.table[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: currently, it slices the right, then the left. This can be
|
|
||||||
// optimized.
|
|
||||||
function slice(from, to, a)
|
|
||||||
{
|
|
||||||
if (from < 0)
|
|
||||||
{
|
|
||||||
from += length(a);
|
|
||||||
}
|
|
||||||
if (to < 0)
|
|
||||||
{
|
|
||||||
to += length(a);
|
|
||||||
}
|
|
||||||
return sliceLeft(from, sliceRight(to, a));
|
|
||||||
}
|
|
||||||
|
|
||||||
function sliceRight(to, a)
|
|
||||||
{
|
|
||||||
if (to === length(a))
|
|
||||||
{
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle leaf level.
|
|
||||||
if (a.height === 0)
|
|
||||||
{
|
|
||||||
var newA = { ctor:'_Array', height:0 };
|
|
||||||
newA.table = a.table.slice(0, to);
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice the right recursively.
|
|
||||||
var right = getSlot(to, a);
|
|
||||||
var sliced = sliceRight(to - (right > 0 ? a.lengths[right - 1] : 0), a.table[right]);
|
|
||||||
|
|
||||||
// Maybe the a node is not even needed, as sliced contains the whole slice.
|
|
||||||
if (right === 0)
|
|
||||||
{
|
|
||||||
return sliced;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new node.
|
|
||||||
var newA = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: a.height,
|
|
||||||
table: a.table.slice(0, right),
|
|
||||||
lengths: a.lengths.slice(0, right)
|
|
||||||
};
|
|
||||||
if (sliced.table.length > 0)
|
|
||||||
{
|
|
||||||
newA.table[right] = sliced;
|
|
||||||
newA.lengths[right] = length(sliced) + (right > 0 ? newA.lengths[right - 1] : 0);
|
|
||||||
}
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sliceLeft(from, a)
|
|
||||||
{
|
|
||||||
if (from === 0)
|
|
||||||
{
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle leaf level.
|
|
||||||
if (a.height === 0)
|
|
||||||
{
|
|
||||||
var newA = { ctor:'_Array', height:0 };
|
|
||||||
newA.table = a.table.slice(from, a.table.length + 1);
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Slice the left recursively.
|
|
||||||
var left = getSlot(from, a);
|
|
||||||
var sliced = sliceLeft(from - (left > 0 ? a.lengths[left - 1] : 0), a.table[left]);
|
|
||||||
|
|
||||||
// Maybe the a node is not even needed, as sliced contains the whole slice.
|
|
||||||
if (left === a.table.length - 1)
|
|
||||||
{
|
|
||||||
return sliced;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create new node.
|
|
||||||
var newA = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: a.height,
|
|
||||||
table: a.table.slice(left, a.table.length + 1),
|
|
||||||
lengths: new Array(a.table.length - left)
|
|
||||||
};
|
|
||||||
newA.table[0] = sliced;
|
|
||||||
var len = 0;
|
|
||||||
for (var i = 0; i < newA.table.length; i++)
|
|
||||||
{
|
|
||||||
len += length(newA.table[i]);
|
|
||||||
newA.lengths[i] = len;
|
|
||||||
}
|
|
||||||
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Appends two trees.
|
|
||||||
function append(a,b)
|
|
||||||
{
|
|
||||||
if (a.table.length === 0)
|
|
||||||
{
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
if (b.table.length === 0)
|
|
||||||
{
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
var c = append_(a, b);
|
|
||||||
|
|
||||||
// Check if both nodes can be crunshed together.
|
|
||||||
if (c[0].table.length + c[1].table.length <= M)
|
|
||||||
{
|
|
||||||
if (c[0].table.length === 0)
|
|
||||||
{
|
|
||||||
return c[1];
|
|
||||||
}
|
|
||||||
if (c[1].table.length === 0)
|
|
||||||
{
|
|
||||||
return c[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adjust .table and .lengths
|
|
||||||
c[0].table = c[0].table.concat(c[1].table);
|
|
||||||
if (c[0].height > 0)
|
|
||||||
{
|
|
||||||
var len = length(c[0]);
|
|
||||||
for (var i = 0; i < c[1].lengths.length; i++)
|
|
||||||
{
|
|
||||||
c[1].lengths[i] += len;
|
|
||||||
}
|
|
||||||
c[0].lengths = c[0].lengths.concat(c[1].lengths);
|
|
||||||
}
|
|
||||||
|
|
||||||
return c[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (c[0].height > 0)
|
|
||||||
{
|
|
||||||
var toRemove = calcToRemove(a, b);
|
|
||||||
if (toRemove > E)
|
|
||||||
{
|
|
||||||
c = shuffle(c[0], c[1], toRemove);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return siblise(c[0], c[1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an array of two nodes; right and left. One node _may_ be empty.
|
|
||||||
function append_(a, b)
|
|
||||||
{
|
|
||||||
if (a.height === 0 && b.height === 0)
|
|
||||||
{
|
|
||||||
return [a, b];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.height !== 1 || b.height !== 1)
|
|
||||||
{
|
|
||||||
if (a.height === b.height)
|
|
||||||
{
|
|
||||||
a = nodeCopy(a);
|
|
||||||
b = nodeCopy(b);
|
|
||||||
var appended = append_(botRight(a), botLeft(b));
|
|
||||||
|
|
||||||
insertRight(a, appended[1]);
|
|
||||||
insertLeft(b, appended[0]);
|
|
||||||
}
|
|
||||||
else if (a.height > b.height)
|
|
||||||
{
|
|
||||||
a = nodeCopy(a);
|
|
||||||
var appended = append_(botRight(a), b);
|
|
||||||
|
|
||||||
insertRight(a, appended[0]);
|
|
||||||
b = parentise(appended[1], appended[1].height + 1);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
b = nodeCopy(b);
|
|
||||||
var appended = append_(a, botLeft(b));
|
|
||||||
|
|
||||||
var left = appended[0].table.length === 0 ? 0 : 1;
|
|
||||||
var right = left === 0 ? 1 : 0;
|
|
||||||
insertLeft(b, appended[left]);
|
|
||||||
a = parentise(appended[right], appended[right].height + 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if balancing is needed and return based on that.
|
|
||||||
if (a.table.length === 0 || b.table.length === 0)
|
|
||||||
{
|
|
||||||
return [a, b];
|
|
||||||
}
|
|
||||||
|
|
||||||
var toRemove = calcToRemove(a, b);
|
|
||||||
if (toRemove <= E)
|
|
||||||
{
|
|
||||||
return [a, b];
|
|
||||||
}
|
|
||||||
return shuffle(a, b, toRemove);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helperfunctions for append_. Replaces a child node at the side of the parent.
|
|
||||||
function insertRight(parent, node)
|
|
||||||
{
|
|
||||||
var index = parent.table.length - 1;
|
|
||||||
parent.table[index] = node;
|
|
||||||
parent.lengths[index] = length(node);
|
|
||||||
parent.lengths[index] += index > 0 ? parent.lengths[index - 1] : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function insertLeft(parent, node)
|
|
||||||
{
|
|
||||||
if (node.table.length > 0)
|
|
||||||
{
|
|
||||||
parent.table[0] = node;
|
|
||||||
parent.lengths[0] = length(node);
|
|
||||||
|
|
||||||
var len = length(parent.table[0]);
|
|
||||||
for (var i = 1; i < parent.lengths.length; i++)
|
|
||||||
{
|
|
||||||
len += length(parent.table[i]);
|
|
||||||
parent.lengths[i] = len;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parent.table.shift();
|
|
||||||
for (var i = 1; i < parent.lengths.length; i++)
|
|
||||||
{
|
|
||||||
parent.lengths[i] = parent.lengths[i] - parent.lengths[0];
|
|
||||||
}
|
|
||||||
parent.lengths.shift();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns the extra search steps for E. Refer to the paper.
|
|
||||||
function calcToRemove(a, b)
|
|
||||||
{
|
|
||||||
var subLengths = 0;
|
|
||||||
for (var i = 0; i < a.table.length; i++)
|
|
||||||
{
|
|
||||||
subLengths += a.table[i].table.length;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < b.table.length; i++)
|
|
||||||
{
|
|
||||||
subLengths += b.table[i].table.length;
|
|
||||||
}
|
|
||||||
|
|
||||||
var toRemove = a.table.length + b.table.length;
|
|
||||||
return toRemove - (Math.floor((subLengths - 1) / M) + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// get2, set2 and saveSlot are helpers for accessing elements over two arrays.
|
|
||||||
function get2(a, b, index)
|
|
||||||
{
|
|
||||||
return index < a.length
|
|
||||||
? a[index]
|
|
||||||
: b[index - a.length];
|
|
||||||
}
|
|
||||||
|
|
||||||
function set2(a, b, index, value)
|
|
||||||
{
|
|
||||||
if (index < a.length)
|
|
||||||
{
|
|
||||||
a[index] = value;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
b[index - a.length] = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function saveSlot(a, b, index, slot)
|
|
||||||
{
|
|
||||||
set2(a.table, b.table, index, slot);
|
|
||||||
|
|
||||||
var l = (index === 0 || index === a.lengths.length)
|
|
||||||
? 0
|
|
||||||
: get2(a.lengths, a.lengths, index - 1);
|
|
||||||
|
|
||||||
set2(a.lengths, b.lengths, index, l + length(slot));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Creates a node or leaf with a given length at their arrays for perfomance.
|
|
||||||
// Is only used by shuffle.
|
|
||||||
function createNode(h, length)
|
|
||||||
{
|
|
||||||
if (length < 0)
|
|
||||||
{
|
|
||||||
length = 0;
|
|
||||||
}
|
|
||||||
var a = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: h,
|
|
||||||
table: new Array(length)
|
|
||||||
};
|
|
||||||
if (h > 0)
|
|
||||||
{
|
|
||||||
a.lengths = new Array(length);
|
|
||||||
}
|
|
||||||
return a;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns an array of two balanced nodes.
|
|
||||||
function shuffle(a, b, toRemove)
|
|
||||||
{
|
|
||||||
var newA = createNode(a.height, Math.min(M, a.table.length + b.table.length - toRemove));
|
|
||||||
var newB = createNode(a.height, newA.table.length - (a.table.length + b.table.length - toRemove));
|
|
||||||
|
|
||||||
// Skip the slots with size M. More precise: copy the slot references
|
|
||||||
// to the new node
|
|
||||||
var read = 0;
|
|
||||||
while (get2(a.table, b.table, read).table.length % M === 0)
|
|
||||||
{
|
|
||||||
set2(newA.table, newB.table, read, get2(a.table, b.table, read));
|
|
||||||
set2(newA.lengths, newB.lengths, read, get2(a.lengths, b.lengths, read));
|
|
||||||
read++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pulling items from left to right, caching in a slot before writing
|
|
||||||
// it into the new nodes.
|
|
||||||
var write = read;
|
|
||||||
var slot = new createNode(a.height - 1, 0);
|
|
||||||
var from = 0;
|
|
||||||
|
|
||||||
// If the current slot is still containing data, then there will be at
|
|
||||||
// least one more write, so we do not break this loop yet.
|
|
||||||
while (read - write - (slot.table.length > 0 ? 1 : 0) < toRemove)
|
|
||||||
{
|
|
||||||
// Find out the max possible items for copying.
|
|
||||||
var source = get2(a.table, b.table, read);
|
|
||||||
var to = Math.min(M - slot.table.length, source.table.length);
|
|
||||||
|
|
||||||
// Copy and adjust size table.
|
|
||||||
slot.table = slot.table.concat(source.table.slice(from, to));
|
|
||||||
if (slot.height > 0)
|
|
||||||
{
|
|
||||||
var len = slot.lengths.length;
|
|
||||||
for (var i = len; i < len + to - from; i++)
|
|
||||||
{
|
|
||||||
slot.lengths[i] = length(slot.table[i]);
|
|
||||||
slot.lengths[i] += (i > 0 ? slot.lengths[i - 1] : 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
from += to;
|
|
||||||
|
|
||||||
// Only proceed to next slots[i] if the current one was
|
|
||||||
// fully copied.
|
|
||||||
if (source.table.length <= to)
|
|
||||||
{
|
|
||||||
read++; from = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only create a new slot if the current one is filled up.
|
|
||||||
if (slot.table.length === M)
|
|
||||||
{
|
|
||||||
saveSlot(newA, newB, write, slot);
|
|
||||||
slot = createNode(a.height - 1, 0);
|
|
||||||
write++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Cleanup after the loop. Copy the last slot into the new nodes.
|
|
||||||
if (slot.table.length > 0)
|
|
||||||
{
|
|
||||||
saveSlot(newA, newB, write, slot);
|
|
||||||
write++;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Shift the untouched slots to the left
|
|
||||||
while (read < a.table.length + b.table.length )
|
|
||||||
{
|
|
||||||
saveSlot(newA, newB, write, get2(a.table, b.table, read));
|
|
||||||
read++;
|
|
||||||
write++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return [newA, newB];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Navigation functions
|
|
||||||
function botRight(a)
|
|
||||||
{
|
|
||||||
return a.table[a.table.length - 1];
|
|
||||||
}
|
|
||||||
function botLeft(a)
|
|
||||||
{
|
|
||||||
return a.table[0];
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copies a node for updating. Note that you should not use this if
|
|
||||||
// only updating only one of "table" or "lengths" for performance reasons.
|
|
||||||
function nodeCopy(a)
|
|
||||||
{
|
|
||||||
var newA = {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: a.height,
|
|
||||||
table: a.table.slice()
|
|
||||||
};
|
|
||||||
if (a.height > 0)
|
|
||||||
{
|
|
||||||
newA.lengths = a.lengths.slice();
|
|
||||||
}
|
|
||||||
return newA;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Returns how many items are in the tree.
|
|
||||||
function length(array)
|
|
||||||
{
|
|
||||||
if (array.height === 0)
|
|
||||||
{
|
|
||||||
return array.table.length;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return array.lengths[array.lengths.length - 1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculates in which slot of "table" the item probably is, then
|
|
||||||
// find the exact slot via forward searching in "lengths". Returns the index.
|
|
||||||
function getSlot(i, a)
|
|
||||||
{
|
|
||||||
var slot = i >> (5 * a.height);
|
|
||||||
while (a.lengths[slot] <= i)
|
|
||||||
{
|
|
||||||
slot++;
|
|
||||||
}
|
|
||||||
return slot;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively creates a tree with a given height containing
|
|
||||||
// only the given item.
|
|
||||||
function create(item, h)
|
|
||||||
{
|
|
||||||
if (h === 0)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: 0,
|
|
||||||
table: [item]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: h,
|
|
||||||
table: [create(item, h - 1)],
|
|
||||||
lengths: [1]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Recursively creates a tree that contains the given tree.
|
|
||||||
function parentise(tree, h)
|
|
||||||
{
|
|
||||||
if (h === tree.height)
|
|
||||||
{
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: h,
|
|
||||||
table: [parentise(tree, h - 1)],
|
|
||||||
lengths: [length(tree)]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Emphasizes blood brotherhood beneath two trees.
|
|
||||||
function siblise(a, b)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: a.height + 1,
|
|
||||||
table: [a, b],
|
|
||||||
lengths: [length(a), length(a) + length(b)]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function toJSArray(a)
|
|
||||||
{
|
|
||||||
var jsArray = new Array(length(a));
|
|
||||||
toJSArray_(jsArray, 0, a);
|
|
||||||
return jsArray;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toJSArray_(jsArray, i, a)
|
|
||||||
{
|
|
||||||
for (var t = 0; t < a.table.length; t++)
|
|
||||||
{
|
|
||||||
if (a.height === 0)
|
|
||||||
{
|
|
||||||
jsArray[i + t] = a.table[t];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var inc = t === 0 ? 0 : a.lengths[t - 1];
|
|
||||||
toJSArray_(jsArray, i + inc, a.table[t]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromJSArray(jsArray)
|
|
||||||
{
|
|
||||||
if (jsArray.length === 0)
|
|
||||||
{
|
|
||||||
return empty;
|
|
||||||
}
|
|
||||||
var h = Math.floor(Math.log(jsArray.length) / Math.log(M));
|
|
||||||
return fromJSArray_(jsArray, h, 0, jsArray.length);
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromJSArray_(jsArray, h, from, to)
|
|
||||||
{
|
|
||||||
if (h === 0)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: 0,
|
|
||||||
table: jsArray.slice(from, to)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var step = Math.pow(M, h);
|
|
||||||
var table = new Array(Math.ceil((to - from) / step));
|
|
||||||
var lengths = new Array(table.length);
|
|
||||||
for (var i = 0; i < table.length; i++)
|
|
||||||
{
|
|
||||||
table[i] = fromJSArray_(jsArray, h - 1, from + (i * step), Math.min(from + ((i + 1) * step), to));
|
|
||||||
lengths[i] = length(table[i]) + (i > 0 ? lengths[i - 1] : 0);
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
ctor: '_Array',
|
|
||||||
height: h,
|
|
||||||
table: table,
|
|
||||||
lengths: lengths
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
empty: empty,
|
|
||||||
fromList: fromList,
|
|
||||||
toList: toList,
|
|
||||||
initialize: F2(initialize),
|
|
||||||
append: F2(append),
|
|
||||||
push: F2(push),
|
|
||||||
slice: F3(slice),
|
|
||||||
get: F2(get),
|
|
||||||
set: F3(set),
|
|
||||||
map: F2(map),
|
|
||||||
indexedMap: F2(indexedMap),
|
|
||||||
foldl: F3(foldl),
|
|
||||||
foldr: F3(foldr),
|
|
||||||
length: length,
|
|
||||||
|
|
||||||
toJSArray: toJSArray,
|
|
||||||
fromJSArray: fromJSArray
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,141 +0,0 @@
|
|||||||
//import Native.Utils //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Basics = function() {
|
|
||||||
|
|
||||||
function div(a, b)
|
|
||||||
{
|
|
||||||
return (a / b) | 0;
|
|
||||||
}
|
|
||||||
function rem(a, b)
|
|
||||||
{
|
|
||||||
return a % b;
|
|
||||||
}
|
|
||||||
function mod(a, b)
|
|
||||||
{
|
|
||||||
if (b === 0)
|
|
||||||
{
|
|
||||||
throw new Error('Cannot perform mod 0. Division by zero error.');
|
|
||||||
}
|
|
||||||
var r = a % b;
|
|
||||||
var m = a === 0 ? 0 : (b > 0 ? (a >= 0 ? r : r + b) : -mod(-a, -b));
|
|
||||||
|
|
||||||
return m === b ? 0 : m;
|
|
||||||
}
|
|
||||||
function logBase(base, n)
|
|
||||||
{
|
|
||||||
return Math.log(n) / Math.log(base);
|
|
||||||
}
|
|
||||||
function negate(n)
|
|
||||||
{
|
|
||||||
return -n;
|
|
||||||
}
|
|
||||||
function abs(n)
|
|
||||||
{
|
|
||||||
return n < 0 ? -n : n;
|
|
||||||
}
|
|
||||||
|
|
||||||
function min(a, b)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_Utils.cmp(a, b) < 0 ? a : b;
|
|
||||||
}
|
|
||||||
function max(a, b)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_Utils.cmp(a, b) > 0 ? a : b;
|
|
||||||
}
|
|
||||||
function clamp(lo, hi, n)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_Utils.cmp(n, lo) < 0
|
|
||||||
? lo
|
|
||||||
: _elm_lang$core$Native_Utils.cmp(n, hi) > 0
|
|
||||||
? hi
|
|
||||||
: n;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ord = ['LT', 'EQ', 'GT'];
|
|
||||||
|
|
||||||
function compare(x, y)
|
|
||||||
{
|
|
||||||
return { ctor: ord[_elm_lang$core$Native_Utils.cmp(x, y) + 1] };
|
|
||||||
}
|
|
||||||
|
|
||||||
function xor(a, b)
|
|
||||||
{
|
|
||||||
return a !== b;
|
|
||||||
}
|
|
||||||
function not(b)
|
|
||||||
{
|
|
||||||
return !b;
|
|
||||||
}
|
|
||||||
function isInfinite(n)
|
|
||||||
{
|
|
||||||
return n === Infinity || n === -Infinity;
|
|
||||||
}
|
|
||||||
|
|
||||||
function truncate(n)
|
|
||||||
{
|
|
||||||
return n | 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
function degrees(d)
|
|
||||||
{
|
|
||||||
return d * Math.PI / 180;
|
|
||||||
}
|
|
||||||
function turns(t)
|
|
||||||
{
|
|
||||||
return 2 * Math.PI * t;
|
|
||||||
}
|
|
||||||
function fromPolar(point)
|
|
||||||
{
|
|
||||||
var r = point._0;
|
|
||||||
var t = point._1;
|
|
||||||
return _elm_lang$core$Native_Utils.Tuple2(r * Math.cos(t), r * Math.sin(t));
|
|
||||||
}
|
|
||||||
function toPolar(point)
|
|
||||||
{
|
|
||||||
var x = point._0;
|
|
||||||
var y = point._1;
|
|
||||||
return _elm_lang$core$Native_Utils.Tuple2(Math.sqrt(x * x + y * y), Math.atan2(y, x));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
div: F2(div),
|
|
||||||
rem: F2(rem),
|
|
||||||
mod: F2(mod),
|
|
||||||
|
|
||||||
pi: Math.PI,
|
|
||||||
e: Math.E,
|
|
||||||
cos: Math.cos,
|
|
||||||
sin: Math.sin,
|
|
||||||
tan: Math.tan,
|
|
||||||
acos: Math.acos,
|
|
||||||
asin: Math.asin,
|
|
||||||
atan: Math.atan,
|
|
||||||
atan2: F2(Math.atan2),
|
|
||||||
|
|
||||||
degrees: degrees,
|
|
||||||
turns: turns,
|
|
||||||
fromPolar: fromPolar,
|
|
||||||
toPolar: toPolar,
|
|
||||||
|
|
||||||
sqrt: Math.sqrt,
|
|
||||||
logBase: F2(logBase),
|
|
||||||
negate: negate,
|
|
||||||
abs: abs,
|
|
||||||
min: F2(min),
|
|
||||||
max: F2(max),
|
|
||||||
clamp: F3(clamp),
|
|
||||||
compare: F2(compare),
|
|
||||||
|
|
||||||
xor: F2(xor),
|
|
||||||
not: not,
|
|
||||||
|
|
||||||
truncate: truncate,
|
|
||||||
ceiling: Math.ceil,
|
|
||||||
floor: Math.floor,
|
|
||||||
round: Math.round,
|
|
||||||
toFloat: function(x) { return x; },
|
|
||||||
isNaN: isNaN,
|
|
||||||
isInfinite: isInfinite
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
var _elm_lang$core$Native_Bitwise = function() {
|
|
||||||
|
|
||||||
return {
|
|
||||||
and: F2(function and(a, b) { return a & b; }),
|
|
||||||
or: F2(function or(a, b) { return a | b; }),
|
|
||||||
xor: F2(function xor(a, b) { return a ^ b; }),
|
|
||||||
complement: function complement(a) { return ~a; },
|
|
||||||
shiftLeftBy: F2(function(offset, a) { return a << offset; }),
|
|
||||||
shiftRightBy: F2(function(offset, a) { return a >> offset; }),
|
|
||||||
shiftRightZfBy: F2(function(offset, a) { return a >>> offset; })
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
//import Native.Utils //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Char = function() {
|
|
||||||
|
|
||||||
return {
|
|
||||||
fromCode: function(c) { return _elm_lang$core$Native_Utils.chr(String.fromCharCode(c)); },
|
|
||||||
toCode: function(c) { return c.charCodeAt(0); },
|
|
||||||
toUpper: function(c) { return _elm_lang$core$Native_Utils.chr(c.toUpperCase()); },
|
|
||||||
toLower: function(c) { return _elm_lang$core$Native_Utils.chr(c.toLowerCase()); },
|
|
||||||
toLocaleUpper: function(c) { return _elm_lang$core$Native_Utils.chr(c.toLocaleUpperCase()); },
|
|
||||||
toLocaleLower: function(c) { return _elm_lang$core$Native_Utils.chr(c.toLocaleLowerCase()); }
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
//import Result //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Date = function() {
|
|
||||||
|
|
||||||
function fromString(str)
|
|
||||||
{
|
|
||||||
var date = new Date(str);
|
|
||||||
return isNaN(date.getTime())
|
|
||||||
? _elm_lang$core$Result$Err('Unable to parse \'' + str + '\' as a date. Dates must be in the ISO 8601 format.')
|
|
||||||
: _elm_lang$core$Result$Ok(date);
|
|
||||||
}
|
|
||||||
|
|
||||||
var dayTable = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];
|
|
||||||
var monthTable =
|
|
||||||
['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun',
|
|
||||||
'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
|
||||||
fromString: fromString,
|
|
||||||
year: function(d) { return d.getFullYear(); },
|
|
||||||
month: function(d) { return { ctor: monthTable[d.getMonth()] }; },
|
|
||||||
day: function(d) { return d.getDate(); },
|
|
||||||
hour: function(d) { return d.getHours(); },
|
|
||||||
minute: function(d) { return d.getMinutes(); },
|
|
||||||
second: function(d) { return d.getSeconds(); },
|
|
||||||
millisecond: function(d) { return d.getMilliseconds(); },
|
|
||||||
toTime: function(d) { return d.getTime(); },
|
|
||||||
fromTime: function(t) { return new Date(t); },
|
|
||||||
dayOfWeek: function(d) { return { ctor: dayTable[d.getDay()] }; }
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
//import Native.Utils //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Debug = function() {
|
|
||||||
|
|
||||||
function log(tag, value)
|
|
||||||
{
|
|
||||||
var msg = tag + ': ' + _elm_lang$core$Native_Utils.toString(value);
|
|
||||||
var process = process || {};
|
|
||||||
if (process.stdout)
|
|
||||||
{
|
|
||||||
process.stdout.write(msg);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
console.log(msg);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function crash(message)
|
|
||||||
{
|
|
||||||
throw new Error(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
crash: crash,
|
|
||||||
log: F2(log)
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,575 +0,0 @@
|
|||||||
//import Maybe, Native.Array, Native.List, Native.Utils, Result //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Json = function() {
|
|
||||||
|
|
||||||
|
|
||||||
// CORE DECODERS
|
|
||||||
|
|
||||||
function succeed(msg)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'succeed',
|
|
||||||
msg: msg
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fail(msg)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'fail',
|
|
||||||
msg: msg
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodePrimitive(tag)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: tag
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeContainer(tag, decoder)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: tag,
|
|
||||||
decoder: decoder
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeNull(value)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'null',
|
|
||||||
value: value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeField(field, decoder)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'field',
|
|
||||||
field: field,
|
|
||||||
decoder: decoder
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeIndex(index, decoder)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'index',
|
|
||||||
index: index,
|
|
||||||
decoder: decoder
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function decodeKeyValuePairs(decoder)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'key-value',
|
|
||||||
decoder: decoder
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function mapMany(f, decoders)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'map-many',
|
|
||||||
func: f,
|
|
||||||
decoders: decoders
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function andThen(callback, decoder)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'andThen',
|
|
||||||
decoder: decoder,
|
|
||||||
callback: callback
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function oneOf(decoders)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '<decoder>',
|
|
||||||
tag: 'oneOf',
|
|
||||||
decoders: decoders
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// DECODING OBJECTS
|
|
||||||
|
|
||||||
function map1(f, d1)
|
|
||||||
{
|
|
||||||
return mapMany(f, [d1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map2(f, d1, d2)
|
|
||||||
{
|
|
||||||
return mapMany(f, [d1, d2]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map3(f, d1, d2, d3)
|
|
||||||
{
|
|
||||||
return mapMany(f, [d1, d2, d3]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map4(f, d1, d2, d3, d4)
|
|
||||||
{
|
|
||||||
return mapMany(f, [d1, d2, d3, d4]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map5(f, d1, d2, d3, d4, d5)
|
|
||||||
{
|
|
||||||
return mapMany(f, [d1, d2, d3, d4, d5]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map6(f, d1, d2, d3, d4, d5, d6)
|
|
||||||
{
|
|
||||||
return mapMany(f, [d1, d2, d3, d4, d5, d6]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map7(f, d1, d2, d3, d4, d5, d6, d7)
|
|
||||||
{
|
|
||||||
return mapMany(f, [d1, d2, d3, d4, d5, d6, d7]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map8(f, d1, d2, d3, d4, d5, d6, d7, d8)
|
|
||||||
{
|
|
||||||
return mapMany(f, [d1, d2, d3, d4, d5, d6, d7, d8]);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// DECODE HELPERS
|
|
||||||
|
|
||||||
function ok(value)
|
|
||||||
{
|
|
||||||
return { tag: 'ok', value: value };
|
|
||||||
}
|
|
||||||
|
|
||||||
function badPrimitive(type, value)
|
|
||||||
{
|
|
||||||
return { tag: 'primitive', type: type, value: value };
|
|
||||||
}
|
|
||||||
|
|
||||||
function badIndex(index, nestedProblems)
|
|
||||||
{
|
|
||||||
return { tag: 'index', index: index, rest: nestedProblems };
|
|
||||||
}
|
|
||||||
|
|
||||||
function badField(field, nestedProblems)
|
|
||||||
{
|
|
||||||
return { tag: 'field', field: field, rest: nestedProblems };
|
|
||||||
}
|
|
||||||
|
|
||||||
function badIndex(index, nestedProblems)
|
|
||||||
{
|
|
||||||
return { tag: 'index', index: index, rest: nestedProblems };
|
|
||||||
}
|
|
||||||
|
|
||||||
function badOneOf(problems)
|
|
||||||
{
|
|
||||||
return { tag: 'oneOf', problems: problems };
|
|
||||||
}
|
|
||||||
|
|
||||||
function bad(msg)
|
|
||||||
{
|
|
||||||
return { tag: 'fail', msg: msg };
|
|
||||||
}
|
|
||||||
|
|
||||||
function badToString(problem)
|
|
||||||
{
|
|
||||||
var context = '_';
|
|
||||||
while (problem)
|
|
||||||
{
|
|
||||||
switch (problem.tag)
|
|
||||||
{
|
|
||||||
case 'primitive':
|
|
||||||
return 'Expecting ' + problem.type
|
|
||||||
+ (context === '_' ? '' : ' at ' + context)
|
|
||||||
+ ' but instead got: ' + jsToString(problem.value);
|
|
||||||
|
|
||||||
case 'index':
|
|
||||||
context += '[' + problem.index + ']';
|
|
||||||
problem = problem.rest;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'field':
|
|
||||||
context += '.' + problem.field;
|
|
||||||
problem = problem.rest;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'oneOf':
|
|
||||||
var problems = problem.problems;
|
|
||||||
for (var i = 0; i < problems.length; i++)
|
|
||||||
{
|
|
||||||
problems[i] = badToString(problems[i]);
|
|
||||||
}
|
|
||||||
return 'I ran into the following problems'
|
|
||||||
+ (context === '_' ? '' : ' at ' + context)
|
|
||||||
+ ':\n\n' + problems.join('\n');
|
|
||||||
|
|
||||||
case 'fail':
|
|
||||||
return 'I ran into a `fail` decoder'
|
|
||||||
+ (context === '_' ? '' : ' at ' + context)
|
|
||||||
+ ': ' + problem.msg;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function jsToString(value)
|
|
||||||
{
|
|
||||||
return value === undefined
|
|
||||||
? 'undefined'
|
|
||||||
: JSON.stringify(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// DECODE
|
|
||||||
|
|
||||||
function runOnString(decoder, string)
|
|
||||||
{
|
|
||||||
var json;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
json = JSON.parse(string);
|
|
||||||
}
|
|
||||||
catch (e)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Result$Err('Given an invalid JSON: ' + e.message);
|
|
||||||
}
|
|
||||||
return run(decoder, json);
|
|
||||||
}
|
|
||||||
|
|
||||||
function run(decoder, value)
|
|
||||||
{
|
|
||||||
var result = runHelp(decoder, value);
|
|
||||||
return (result.tag === 'ok')
|
|
||||||
? _elm_lang$core$Result$Ok(result.value)
|
|
||||||
: _elm_lang$core$Result$Err(badToString(result));
|
|
||||||
}
|
|
||||||
|
|
||||||
function runHelp(decoder, value)
|
|
||||||
{
|
|
||||||
switch (decoder.tag)
|
|
||||||
{
|
|
||||||
case 'bool':
|
|
||||||
return (typeof value === 'boolean')
|
|
||||||
? ok(value)
|
|
||||||
: badPrimitive('a Bool', value);
|
|
||||||
|
|
||||||
case 'int':
|
|
||||||
if (typeof value !== 'number') {
|
|
||||||
return badPrimitive('an Int', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (-2147483647 < value && value < 2147483647 && (value | 0) === value) {
|
|
||||||
return ok(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isFinite(value) && !(value % 1)) {
|
|
||||||
return ok(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
return badPrimitive('an Int', value);
|
|
||||||
|
|
||||||
case 'float':
|
|
||||||
return (typeof value === 'number')
|
|
||||||
? ok(value)
|
|
||||||
: badPrimitive('a Float', value);
|
|
||||||
|
|
||||||
case 'string':
|
|
||||||
return (typeof value === 'string')
|
|
||||||
? ok(value)
|
|
||||||
: (value instanceof String)
|
|
||||||
? ok(value + '')
|
|
||||||
: badPrimitive('a String', value);
|
|
||||||
|
|
||||||
case 'null':
|
|
||||||
return (value === null)
|
|
||||||
? ok(decoder.value)
|
|
||||||
: badPrimitive('null', value);
|
|
||||||
|
|
||||||
case 'value':
|
|
||||||
return ok(value);
|
|
||||||
|
|
||||||
case 'list':
|
|
||||||
if (!(value instanceof Array))
|
|
||||||
{
|
|
||||||
return badPrimitive('a List', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var list = _elm_lang$core$Native_List.Nil;
|
|
||||||
for (var i = value.length; i--; )
|
|
||||||
{
|
|
||||||
var result = runHelp(decoder.decoder, value[i]);
|
|
||||||
if (result.tag !== 'ok')
|
|
||||||
{
|
|
||||||
return badIndex(i, result)
|
|
||||||
}
|
|
||||||
list = _elm_lang$core$Native_List.Cons(result.value, list);
|
|
||||||
}
|
|
||||||
return ok(list);
|
|
||||||
|
|
||||||
case 'array':
|
|
||||||
if (!(value instanceof Array))
|
|
||||||
{
|
|
||||||
return badPrimitive('an Array', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var len = value.length;
|
|
||||||
var array = new Array(len);
|
|
||||||
for (var i = len; i--; )
|
|
||||||
{
|
|
||||||
var result = runHelp(decoder.decoder, value[i]);
|
|
||||||
if (result.tag !== 'ok')
|
|
||||||
{
|
|
||||||
return badIndex(i, result);
|
|
||||||
}
|
|
||||||
array[i] = result.value;
|
|
||||||
}
|
|
||||||
return ok(_elm_lang$core$Native_Array.fromJSArray(array));
|
|
||||||
|
|
||||||
case 'maybe':
|
|
||||||
var result = runHelp(decoder.decoder, value);
|
|
||||||
return (result.tag === 'ok')
|
|
||||||
? ok(_elm_lang$core$Maybe$Just(result.value))
|
|
||||||
: ok(_elm_lang$core$Maybe$Nothing);
|
|
||||||
|
|
||||||
case 'field':
|
|
||||||
var field = decoder.field;
|
|
||||||
if (typeof value !== 'object' || value === null || !(field in value))
|
|
||||||
{
|
|
||||||
return badPrimitive('an object with a field named `' + field + '`', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = runHelp(decoder.decoder, value[field]);
|
|
||||||
return (result.tag === 'ok') ? result : badField(field, result);
|
|
||||||
|
|
||||||
case 'index':
|
|
||||||
var index = decoder.index;
|
|
||||||
if (!(value instanceof Array))
|
|
||||||
{
|
|
||||||
return badPrimitive('an array', value);
|
|
||||||
}
|
|
||||||
if (index >= value.length)
|
|
||||||
{
|
|
||||||
return badPrimitive('a longer array. Need index ' + index + ' but there are only ' + value.length + ' entries', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = runHelp(decoder.decoder, value[index]);
|
|
||||||
return (result.tag === 'ok') ? result : badIndex(index, result);
|
|
||||||
|
|
||||||
case 'key-value':
|
|
||||||
if (typeof value !== 'object' || value === null || value instanceof Array)
|
|
||||||
{
|
|
||||||
return badPrimitive('an object', value);
|
|
||||||
}
|
|
||||||
|
|
||||||
var keyValuePairs = _elm_lang$core$Native_List.Nil;
|
|
||||||
for (var key in value)
|
|
||||||
{
|
|
||||||
var result = runHelp(decoder.decoder, value[key]);
|
|
||||||
if (result.tag !== 'ok')
|
|
||||||
{
|
|
||||||
return badField(key, result);
|
|
||||||
}
|
|
||||||
var pair = _elm_lang$core$Native_Utils.Tuple2(key, result.value);
|
|
||||||
keyValuePairs = _elm_lang$core$Native_List.Cons(pair, keyValuePairs);
|
|
||||||
}
|
|
||||||
return ok(keyValuePairs);
|
|
||||||
|
|
||||||
case 'map-many':
|
|
||||||
var answer = decoder.func;
|
|
||||||
var decoders = decoder.decoders;
|
|
||||||
for (var i = 0; i < decoders.length; i++)
|
|
||||||
{
|
|
||||||
var result = runHelp(decoders[i], value);
|
|
||||||
if (result.tag !== 'ok')
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
answer = answer(result.value);
|
|
||||||
}
|
|
||||||
return ok(answer);
|
|
||||||
|
|
||||||
case 'andThen':
|
|
||||||
var result = runHelp(decoder.decoder, value);
|
|
||||||
return (result.tag !== 'ok')
|
|
||||||
? result
|
|
||||||
: runHelp(decoder.callback(result.value), value);
|
|
||||||
|
|
||||||
case 'oneOf':
|
|
||||||
var errors = [];
|
|
||||||
var temp = decoder.decoders;
|
|
||||||
while (temp.ctor !== '[]')
|
|
||||||
{
|
|
||||||
var result = runHelp(temp._0, value);
|
|
||||||
|
|
||||||
if (result.tag === 'ok')
|
|
||||||
{
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
errors.push(result);
|
|
||||||
|
|
||||||
temp = temp._1;
|
|
||||||
}
|
|
||||||
return badOneOf(errors);
|
|
||||||
|
|
||||||
case 'fail':
|
|
||||||
return bad(decoder.msg);
|
|
||||||
|
|
||||||
case 'succeed':
|
|
||||||
return ok(decoder.msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// EQUALITY
|
|
||||||
|
|
||||||
function equality(a, b)
|
|
||||||
{
|
|
||||||
if (a === b)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (a.tag !== b.tag)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (a.tag)
|
|
||||||
{
|
|
||||||
case 'succeed':
|
|
||||||
case 'fail':
|
|
||||||
return a.msg === b.msg;
|
|
||||||
|
|
||||||
case 'bool':
|
|
||||||
case 'int':
|
|
||||||
case 'float':
|
|
||||||
case 'string':
|
|
||||||
case 'value':
|
|
||||||
return true;
|
|
||||||
|
|
||||||
case 'null':
|
|
||||||
return a.value === b.value;
|
|
||||||
|
|
||||||
case 'list':
|
|
||||||
case 'array':
|
|
||||||
case 'maybe':
|
|
||||||
case 'key-value':
|
|
||||||
return equality(a.decoder, b.decoder);
|
|
||||||
|
|
||||||
case 'field':
|
|
||||||
return a.field === b.field && equality(a.decoder, b.decoder);
|
|
||||||
|
|
||||||
case 'index':
|
|
||||||
return a.index === b.index && equality(a.decoder, b.decoder);
|
|
||||||
|
|
||||||
case 'map-many':
|
|
||||||
if (a.func !== b.func)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return listEquality(a.decoders, b.decoders);
|
|
||||||
|
|
||||||
case 'andThen':
|
|
||||||
return a.callback === b.callback && equality(a.decoder, b.decoder);
|
|
||||||
|
|
||||||
case 'oneOf':
|
|
||||||
return listEquality(a.decoders, b.decoders);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function listEquality(aDecoders, bDecoders)
|
|
||||||
{
|
|
||||||
var len = aDecoders.length;
|
|
||||||
if (len !== bDecoders.length)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < len; i++)
|
|
||||||
{
|
|
||||||
if (!equality(aDecoders[i], bDecoders[i]))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// ENCODE
|
|
||||||
|
|
||||||
function encode(indentLevel, value)
|
|
||||||
{
|
|
||||||
return JSON.stringify(value, null, indentLevel);
|
|
||||||
}
|
|
||||||
|
|
||||||
function identity(value)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
function encodeObject(keyValuePairs)
|
|
||||||
{
|
|
||||||
var obj = {};
|
|
||||||
while (keyValuePairs.ctor !== '[]')
|
|
||||||
{
|
|
||||||
var pair = keyValuePairs._0;
|
|
||||||
obj[pair._0] = pair._1;
|
|
||||||
keyValuePairs = keyValuePairs._1;
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
encode: F2(encode),
|
|
||||||
runOnString: F2(runOnString),
|
|
||||||
run: F2(run),
|
|
||||||
|
|
||||||
decodeNull: decodeNull,
|
|
||||||
decodePrimitive: decodePrimitive,
|
|
||||||
decodeContainer: F2(decodeContainer),
|
|
||||||
|
|
||||||
decodeField: F2(decodeField),
|
|
||||||
decodeIndex: F2(decodeIndex),
|
|
||||||
|
|
||||||
map1: F2(map1),
|
|
||||||
map2: F3(map2),
|
|
||||||
map3: F4(map3),
|
|
||||||
map4: F5(map4),
|
|
||||||
map5: F6(map5),
|
|
||||||
map6: F7(map6),
|
|
||||||
map7: F8(map7),
|
|
||||||
map8: F9(map8),
|
|
||||||
decodeKeyValuePairs: decodeKeyValuePairs,
|
|
||||||
|
|
||||||
andThen: F2(andThen),
|
|
||||||
fail: fail,
|
|
||||||
succeed: succeed,
|
|
||||||
oneOf: oneOf,
|
|
||||||
|
|
||||||
identity: identity,
|
|
||||||
encodeNull: null,
|
|
||||||
encodeArray: _elm_lang$core$Native_Array.toJSArray,
|
|
||||||
encodeList: _elm_lang$core$Native_List.toArray,
|
|
||||||
encodeObject: encodeObject,
|
|
||||||
|
|
||||||
equality: equality
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,137 +0,0 @@
|
|||||||
//import Native.Utils //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_List = function() {
|
|
||||||
|
|
||||||
var Nil = { ctor: '[]' };
|
|
||||||
|
|
||||||
function Cons(hd, tl)
|
|
||||||
{
|
|
||||||
return { ctor: '::', _0: hd, _1: tl };
|
|
||||||
}
|
|
||||||
|
|
||||||
function fromArray(arr)
|
|
||||||
{
|
|
||||||
var out = Nil;
|
|
||||||
for (var i = arr.length; i--; )
|
|
||||||
{
|
|
||||||
out = Cons(arr[i], out);
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
function toArray(xs)
|
|
||||||
{
|
|
||||||
var out = [];
|
|
||||||
while (xs.ctor !== '[]')
|
|
||||||
{
|
|
||||||
out.push(xs._0);
|
|
||||||
xs = xs._1;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
function foldr(f, b, xs)
|
|
||||||
{
|
|
||||||
var arr = toArray(xs);
|
|
||||||
var acc = b;
|
|
||||||
for (var i = arr.length; i--; )
|
|
||||||
{
|
|
||||||
acc = A2(f, arr[i], acc);
|
|
||||||
}
|
|
||||||
return acc;
|
|
||||||
}
|
|
||||||
|
|
||||||
function map2(f, xs, ys)
|
|
||||||
{
|
|
||||||
var arr = [];
|
|
||||||
while (xs.ctor !== '[]' && ys.ctor !== '[]')
|
|
||||||
{
|
|
||||||
arr.push(A2(f, xs._0, ys._0));
|
|
||||||
xs = xs._1;
|
|
||||||
ys = ys._1;
|
|
||||||
}
|
|
||||||
return fromArray(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map3(f, xs, ys, zs)
|
|
||||||
{
|
|
||||||
var arr = [];
|
|
||||||
while (xs.ctor !== '[]' && ys.ctor !== '[]' && zs.ctor !== '[]')
|
|
||||||
{
|
|
||||||
arr.push(A3(f, xs._0, ys._0, zs._0));
|
|
||||||
xs = xs._1;
|
|
||||||
ys = ys._1;
|
|
||||||
zs = zs._1;
|
|
||||||
}
|
|
||||||
return fromArray(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map4(f, ws, xs, ys, zs)
|
|
||||||
{
|
|
||||||
var arr = [];
|
|
||||||
while ( ws.ctor !== '[]'
|
|
||||||
&& xs.ctor !== '[]'
|
|
||||||
&& ys.ctor !== '[]'
|
|
||||||
&& zs.ctor !== '[]')
|
|
||||||
{
|
|
||||||
arr.push(A4(f, ws._0, xs._0, ys._0, zs._0));
|
|
||||||
ws = ws._1;
|
|
||||||
xs = xs._1;
|
|
||||||
ys = ys._1;
|
|
||||||
zs = zs._1;
|
|
||||||
}
|
|
||||||
return fromArray(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
function map5(f, vs, ws, xs, ys, zs)
|
|
||||||
{
|
|
||||||
var arr = [];
|
|
||||||
while ( vs.ctor !== '[]'
|
|
||||||
&& ws.ctor !== '[]'
|
|
||||||
&& xs.ctor !== '[]'
|
|
||||||
&& ys.ctor !== '[]'
|
|
||||||
&& zs.ctor !== '[]')
|
|
||||||
{
|
|
||||||
arr.push(A5(f, vs._0, ws._0, xs._0, ys._0, zs._0));
|
|
||||||
vs = vs._1;
|
|
||||||
ws = ws._1;
|
|
||||||
xs = xs._1;
|
|
||||||
ys = ys._1;
|
|
||||||
zs = zs._1;
|
|
||||||
}
|
|
||||||
return fromArray(arr);
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortBy(f, xs)
|
|
||||||
{
|
|
||||||
return fromArray(toArray(xs).sort(function(a, b) {
|
|
||||||
return _elm_lang$core$Native_Utils.cmp(f(a), f(b));
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
function sortWith(f, xs)
|
|
||||||
{
|
|
||||||
return fromArray(toArray(xs).sort(function(a, b) {
|
|
||||||
var ord = f(a)(b).ctor;
|
|
||||||
return ord === 'EQ' ? 0 : ord === 'LT' ? -1 : 1;
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
Nil: Nil,
|
|
||||||
Cons: Cons,
|
|
||||||
cons: F2(Cons),
|
|
||||||
toArray: toArray,
|
|
||||||
fromArray: fromArray,
|
|
||||||
|
|
||||||
foldr: F3(foldr),
|
|
||||||
|
|
||||||
map2: F3(map2),
|
|
||||||
map3: F4(map3),
|
|
||||||
map4: F5(map4),
|
|
||||||
map5: F6(map5),
|
|
||||||
sortBy: F2(sortBy),
|
|
||||||
sortWith: F2(sortWith)
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,559 +0,0 @@
|
|||||||
//import //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Platform = function() {
|
|
||||||
|
|
||||||
|
|
||||||
// PROGRAMS
|
|
||||||
|
|
||||||
function program(impl)
|
|
||||||
{
|
|
||||||
return function(flagDecoder)
|
|
||||||
{
|
|
||||||
return function(object, moduleName)
|
|
||||||
{
|
|
||||||
object['worker'] = function worker(flags)
|
|
||||||
{
|
|
||||||
if (typeof flags !== 'undefined')
|
|
||||||
{
|
|
||||||
throw new Error(
|
|
||||||
'The `' + moduleName + '` module does not need flags.\n'
|
|
||||||
+ 'Call ' + moduleName + '.worker() with no arguments and you should be all set!'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialize(
|
|
||||||
impl.init,
|
|
||||||
impl.update,
|
|
||||||
impl.subscriptions,
|
|
||||||
renderer
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function programWithFlags(impl)
|
|
||||||
{
|
|
||||||
return function(flagDecoder)
|
|
||||||
{
|
|
||||||
return function(object, moduleName)
|
|
||||||
{
|
|
||||||
object['worker'] = function worker(flags)
|
|
||||||
{
|
|
||||||
if (typeof flagDecoder === 'undefined')
|
|
||||||
{
|
|
||||||
throw new Error(
|
|
||||||
'Are you trying to sneak a Never value into Elm? Trickster!\n'
|
|
||||||
+ 'It looks like ' + moduleName + '.main is defined with `programWithFlags` but has type `Program Never`.\n'
|
|
||||||
+ 'Use `program` instead if you do not want flags.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = A2(_elm_lang$core$Native_Json.run, flagDecoder, flags);
|
|
||||||
if (result.ctor === 'Err')
|
|
||||||
{
|
|
||||||
throw new Error(
|
|
||||||
moduleName + '.worker(...) was called with an unexpected argument.\n'
|
|
||||||
+ 'I tried to convert it to an Elm value, but ran into this problem:\n\n'
|
|
||||||
+ result._0
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return initialize(
|
|
||||||
impl.init(result._0),
|
|
||||||
impl.update,
|
|
||||||
impl.subscriptions,
|
|
||||||
renderer
|
|
||||||
);
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function renderer(enqueue, _)
|
|
||||||
{
|
|
||||||
return function(_) {};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// HTML TO PROGRAM
|
|
||||||
|
|
||||||
function htmlToProgram(vnode)
|
|
||||||
{
|
|
||||||
var emptyBag = batch(_elm_lang$core$Native_List.Nil);
|
|
||||||
var noChange = _elm_lang$core$Native_Utils.Tuple2(
|
|
||||||
_elm_lang$core$Native_Utils.Tuple0,
|
|
||||||
emptyBag
|
|
||||||
);
|
|
||||||
|
|
||||||
return _elm_lang$virtual_dom$VirtualDom$program({
|
|
||||||
init: noChange,
|
|
||||||
view: function(model) { return main; },
|
|
||||||
update: F2(function(msg, model) { return noChange; }),
|
|
||||||
subscriptions: function (model) { return emptyBag; }
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// INITIALIZE A PROGRAM
|
|
||||||
|
|
||||||
function initialize(init, update, subscriptions, renderer)
|
|
||||||
{
|
|
||||||
// ambient state
|
|
||||||
var managers = {};
|
|
||||||
var updateView;
|
|
||||||
|
|
||||||
// init and update state in main process
|
|
||||||
var initApp = _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) {
|
|
||||||
var model = init._0;
|
|
||||||
updateView = renderer(enqueue, model);
|
|
||||||
var cmds = init._1;
|
|
||||||
var subs = subscriptions(model);
|
|
||||||
dispatchEffects(managers, cmds, subs);
|
|
||||||
callback(_elm_lang$core$Native_Scheduler.succeed(model));
|
|
||||||
});
|
|
||||||
|
|
||||||
function onMessage(msg, model)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback) {
|
|
||||||
var results = A2(update, msg, model);
|
|
||||||
model = results._0;
|
|
||||||
updateView(model);
|
|
||||||
var cmds = results._1;
|
|
||||||
var subs = subscriptions(model);
|
|
||||||
dispatchEffects(managers, cmds, subs);
|
|
||||||
callback(_elm_lang$core$Native_Scheduler.succeed(model));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
var mainProcess = spawnLoop(initApp, onMessage);
|
|
||||||
|
|
||||||
function enqueue(msg)
|
|
||||||
{
|
|
||||||
_elm_lang$core$Native_Scheduler.rawSend(mainProcess, msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ports = setupEffects(managers, enqueue);
|
|
||||||
|
|
||||||
return ports ? { ports: ports } : {};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// EFFECT MANAGERS
|
|
||||||
|
|
||||||
var effectManagers = {};
|
|
||||||
|
|
||||||
function setupEffects(managers, callback)
|
|
||||||
{
|
|
||||||
var ports;
|
|
||||||
|
|
||||||
// setup all necessary effect managers
|
|
||||||
for (var key in effectManagers)
|
|
||||||
{
|
|
||||||
var manager = effectManagers[key];
|
|
||||||
|
|
||||||
if (manager.isForeign)
|
|
||||||
{
|
|
||||||
ports = ports || {};
|
|
||||||
ports[key] = manager.tag === 'cmd'
|
|
||||||
? setupOutgoingPort(key)
|
|
||||||
: setupIncomingPort(key, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
managers[key] = makeManager(manager, callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ports;
|
|
||||||
}
|
|
||||||
|
|
||||||
function makeManager(info, callback)
|
|
||||||
{
|
|
||||||
var router = {
|
|
||||||
main: callback,
|
|
||||||
self: undefined
|
|
||||||
};
|
|
||||||
|
|
||||||
var tag = info.tag;
|
|
||||||
var onEffects = info.onEffects;
|
|
||||||
var onSelfMsg = info.onSelfMsg;
|
|
||||||
|
|
||||||
function onMessage(msg, state)
|
|
||||||
{
|
|
||||||
if (msg.ctor === 'self')
|
|
||||||
{
|
|
||||||
return A3(onSelfMsg, router, msg._0, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fx = msg._0;
|
|
||||||
switch (tag)
|
|
||||||
{
|
|
||||||
case 'cmd':
|
|
||||||
return A3(onEffects, router, fx.cmds, state);
|
|
||||||
|
|
||||||
case 'sub':
|
|
||||||
return A3(onEffects, router, fx.subs, state);
|
|
||||||
|
|
||||||
case 'fx':
|
|
||||||
return A4(onEffects, router, fx.cmds, fx.subs, state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var process = spawnLoop(info.init, onMessage);
|
|
||||||
router.self = process;
|
|
||||||
return process;
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendToApp(router, msg)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback)
|
|
||||||
{
|
|
||||||
router.main(msg);
|
|
||||||
callback(_elm_lang$core$Native_Scheduler.succeed(_elm_lang$core$Native_Utils.Tuple0));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sendToSelf(router, msg)
|
|
||||||
{
|
|
||||||
return A2(_elm_lang$core$Native_Scheduler.send, router.self, {
|
|
||||||
ctor: 'self',
|
|
||||||
_0: msg
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// HELPER for STATEFUL LOOPS
|
|
||||||
|
|
||||||
function spawnLoop(init, onMessage)
|
|
||||||
{
|
|
||||||
var andThen = _elm_lang$core$Native_Scheduler.andThen;
|
|
||||||
|
|
||||||
function loop(state)
|
|
||||||
{
|
|
||||||
var handleMsg = _elm_lang$core$Native_Scheduler.receive(function(msg) {
|
|
||||||
return onMessage(msg, state);
|
|
||||||
});
|
|
||||||
return A2(andThen, loop, handleMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
var task = A2(andThen, loop, init);
|
|
||||||
|
|
||||||
return _elm_lang$core$Native_Scheduler.rawSpawn(task);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// BAGS
|
|
||||||
|
|
||||||
function leaf(home)
|
|
||||||
{
|
|
||||||
return function(value)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
type: 'leaf',
|
|
||||||
home: home,
|
|
||||||
value: value
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function batch(list)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
type: 'node',
|
|
||||||
branches: list
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function map(tagger, bag)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
type: 'map',
|
|
||||||
tagger: tagger,
|
|
||||||
tree: bag
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// PIPE BAGS INTO EFFECT MANAGERS
|
|
||||||
|
|
||||||
function dispatchEffects(managers, cmdBag, subBag)
|
|
||||||
{
|
|
||||||
var effectsDict = {};
|
|
||||||
gatherEffects(true, cmdBag, effectsDict, null);
|
|
||||||
gatherEffects(false, subBag, effectsDict, null);
|
|
||||||
|
|
||||||
for (var home in managers)
|
|
||||||
{
|
|
||||||
var fx = home in effectsDict
|
|
||||||
? effectsDict[home]
|
|
||||||
: {
|
|
||||||
cmds: _elm_lang$core$Native_List.Nil,
|
|
||||||
subs: _elm_lang$core$Native_List.Nil
|
|
||||||
};
|
|
||||||
|
|
||||||
_elm_lang$core$Native_Scheduler.rawSend(managers[home], { ctor: 'fx', _0: fx });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function gatherEffects(isCmd, bag, effectsDict, taggers)
|
|
||||||
{
|
|
||||||
switch (bag.type)
|
|
||||||
{
|
|
||||||
case 'leaf':
|
|
||||||
var home = bag.home;
|
|
||||||
var effect = toEffect(isCmd, home, taggers, bag.value);
|
|
||||||
effectsDict[home] = insert(isCmd, effect, effectsDict[home]);
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 'node':
|
|
||||||
var list = bag.branches;
|
|
||||||
while (list.ctor !== '[]')
|
|
||||||
{
|
|
||||||
gatherEffects(isCmd, list._0, effectsDict, taggers);
|
|
||||||
list = list._1;
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
|
|
||||||
case 'map':
|
|
||||||
gatherEffects(isCmd, bag.tree, effectsDict, {
|
|
||||||
tagger: bag.tagger,
|
|
||||||
rest: taggers
|
|
||||||
});
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function toEffect(isCmd, home, taggers, value)
|
|
||||||
{
|
|
||||||
function applyTaggers(x)
|
|
||||||
{
|
|
||||||
var temp = taggers;
|
|
||||||
while (temp)
|
|
||||||
{
|
|
||||||
x = temp.tagger(x);
|
|
||||||
temp = temp.rest;
|
|
||||||
}
|
|
||||||
return x;
|
|
||||||
}
|
|
||||||
|
|
||||||
var map = isCmd
|
|
||||||
? effectManagers[home].cmdMap
|
|
||||||
: effectManagers[home].subMap;
|
|
||||||
|
|
||||||
return A2(map, applyTaggers, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
function insert(isCmd, newEffect, effects)
|
|
||||||
{
|
|
||||||
effects = effects || {
|
|
||||||
cmds: _elm_lang$core$Native_List.Nil,
|
|
||||||
subs: _elm_lang$core$Native_List.Nil
|
|
||||||
};
|
|
||||||
if (isCmd)
|
|
||||||
{
|
|
||||||
effects.cmds = _elm_lang$core$Native_List.Cons(newEffect, effects.cmds);
|
|
||||||
return effects;
|
|
||||||
}
|
|
||||||
effects.subs = _elm_lang$core$Native_List.Cons(newEffect, effects.subs);
|
|
||||||
return effects;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// PORTS
|
|
||||||
|
|
||||||
function checkPortName(name)
|
|
||||||
{
|
|
||||||
if (name in effectManagers)
|
|
||||||
{
|
|
||||||
throw new Error('There can only be one port named `' + name + '`, but your program has multiple.');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// OUTGOING PORTS
|
|
||||||
|
|
||||||
function outgoingPort(name, converter)
|
|
||||||
{
|
|
||||||
checkPortName(name);
|
|
||||||
effectManagers[name] = {
|
|
||||||
tag: 'cmd',
|
|
||||||
cmdMap: outgoingPortMap,
|
|
||||||
converter: converter,
|
|
||||||
isForeign: true
|
|
||||||
};
|
|
||||||
return leaf(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
var outgoingPortMap = F2(function cmdMap(tagger, value) {
|
|
||||||
return value;
|
|
||||||
});
|
|
||||||
|
|
||||||
function setupOutgoingPort(name)
|
|
||||||
{
|
|
||||||
var subs = [];
|
|
||||||
var converter = effectManagers[name].converter;
|
|
||||||
|
|
||||||
// CREATE MANAGER
|
|
||||||
|
|
||||||
var init = _elm_lang$core$Native_Scheduler.succeed(null);
|
|
||||||
|
|
||||||
function onEffects(router, cmdList, state)
|
|
||||||
{
|
|
||||||
while (cmdList.ctor !== '[]')
|
|
||||||
{
|
|
||||||
// grab a separate reference to subs in case unsubscribe is called
|
|
||||||
var currentSubs = subs;
|
|
||||||
var value = converter(cmdList._0);
|
|
||||||
for (var i = 0; i < currentSubs.length; i++)
|
|
||||||
{
|
|
||||||
currentSubs[i](value);
|
|
||||||
}
|
|
||||||
cmdList = cmdList._1;
|
|
||||||
}
|
|
||||||
return init;
|
|
||||||
}
|
|
||||||
|
|
||||||
effectManagers[name].init = init;
|
|
||||||
effectManagers[name].onEffects = F3(onEffects);
|
|
||||||
|
|
||||||
// PUBLIC API
|
|
||||||
|
|
||||||
function subscribe(callback)
|
|
||||||
{
|
|
||||||
subs.push(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
function unsubscribe(callback)
|
|
||||||
{
|
|
||||||
// copy subs into a new array in case unsubscribe is called within a
|
|
||||||
// subscribed callback
|
|
||||||
subs = subs.slice();
|
|
||||||
var index = subs.indexOf(callback);
|
|
||||||
if (index >= 0)
|
|
||||||
{
|
|
||||||
subs.splice(index, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
subscribe: subscribe,
|
|
||||||
unsubscribe: unsubscribe
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// INCOMING PORTS
|
|
||||||
|
|
||||||
function incomingPort(name, converter)
|
|
||||||
{
|
|
||||||
checkPortName(name);
|
|
||||||
effectManagers[name] = {
|
|
||||||
tag: 'sub',
|
|
||||||
subMap: incomingPortMap,
|
|
||||||
converter: converter,
|
|
||||||
isForeign: true
|
|
||||||
};
|
|
||||||
return leaf(name);
|
|
||||||
}
|
|
||||||
|
|
||||||
var incomingPortMap = F2(function subMap(tagger, finalTagger)
|
|
||||||
{
|
|
||||||
return function(value)
|
|
||||||
{
|
|
||||||
return tagger(finalTagger(value));
|
|
||||||
};
|
|
||||||
});
|
|
||||||
|
|
||||||
function setupIncomingPort(name, callback)
|
|
||||||
{
|
|
||||||
var sentBeforeInit = [];
|
|
||||||
var subs = _elm_lang$core$Native_List.Nil;
|
|
||||||
var converter = effectManagers[name].converter;
|
|
||||||
var currentOnEffects = preInitOnEffects;
|
|
||||||
var currentSend = preInitSend;
|
|
||||||
|
|
||||||
// CREATE MANAGER
|
|
||||||
|
|
||||||
var init = _elm_lang$core$Native_Scheduler.succeed(null);
|
|
||||||
|
|
||||||
function preInitOnEffects(router, subList, state)
|
|
||||||
{
|
|
||||||
var postInitResult = postInitOnEffects(router, subList, state);
|
|
||||||
|
|
||||||
for(var i = 0; i < sentBeforeInit.length; i++)
|
|
||||||
{
|
|
||||||
postInitSend(sentBeforeInit[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
sentBeforeInit = null; // to release objects held in queue
|
|
||||||
currentSend = postInitSend;
|
|
||||||
currentOnEffects = postInitOnEffects;
|
|
||||||
return postInitResult;
|
|
||||||
}
|
|
||||||
|
|
||||||
function postInitOnEffects(router, subList, state)
|
|
||||||
{
|
|
||||||
subs = subList;
|
|
||||||
return init;
|
|
||||||
}
|
|
||||||
|
|
||||||
function onEffects(router, subList, state)
|
|
||||||
{
|
|
||||||
return currentOnEffects(router, subList, state);
|
|
||||||
}
|
|
||||||
|
|
||||||
effectManagers[name].init = init;
|
|
||||||
effectManagers[name].onEffects = F3(onEffects);
|
|
||||||
|
|
||||||
// PUBLIC API
|
|
||||||
|
|
||||||
function preInitSend(value)
|
|
||||||
{
|
|
||||||
sentBeforeInit.push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function postInitSend(value)
|
|
||||||
{
|
|
||||||
var temp = subs;
|
|
||||||
while (temp.ctor !== '[]')
|
|
||||||
{
|
|
||||||
callback(temp._0(value));
|
|
||||||
temp = temp._1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(incomingValue)
|
|
||||||
{
|
|
||||||
var result = A2(_elm_lang$core$Json_Decode$decodeValue, converter, incomingValue);
|
|
||||||
if (result.ctor === 'Err')
|
|
||||||
{
|
|
||||||
throw new Error('Trying to send an unexpected type of value through port `' + name + '`:\n' + result._0);
|
|
||||||
}
|
|
||||||
|
|
||||||
currentSend(result._0);
|
|
||||||
}
|
|
||||||
|
|
||||||
return { send: send };
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
// routers
|
|
||||||
sendToApp: F2(sendToApp),
|
|
||||||
sendToSelf: F2(sendToSelf),
|
|
||||||
|
|
||||||
// global setup
|
|
||||||
effectManagers: effectManagers,
|
|
||||||
outgoingPort: outgoingPort,
|
|
||||||
incomingPort: incomingPort,
|
|
||||||
|
|
||||||
htmlToProgram: htmlToProgram,
|
|
||||||
program: program,
|
|
||||||
programWithFlags: programWithFlags,
|
|
||||||
initialize: initialize,
|
|
||||||
|
|
||||||
// effect bags
|
|
||||||
leaf: leaf,
|
|
||||||
batch: batch,
|
|
||||||
map: F2(map)
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
//import Maybe, Native.List //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Regex = function() {
|
|
||||||
|
|
||||||
function escape(str)
|
|
||||||
{
|
|
||||||
return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
||||||
}
|
|
||||||
function caseInsensitive(re)
|
|
||||||
{
|
|
||||||
return new RegExp(re.source, 'gi');
|
|
||||||
}
|
|
||||||
function regex(raw)
|
|
||||||
{
|
|
||||||
return new RegExp(raw, 'g');
|
|
||||||
}
|
|
||||||
|
|
||||||
function contains(re, string)
|
|
||||||
{
|
|
||||||
return string.match(re) !== null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function find(n, re, str)
|
|
||||||
{
|
|
||||||
n = n.ctor === 'All' ? Infinity : n._0;
|
|
||||||
var out = [];
|
|
||||||
var number = 0;
|
|
||||||
var string = str;
|
|
||||||
var lastIndex = re.lastIndex;
|
|
||||||
var prevLastIndex = -1;
|
|
||||||
var result;
|
|
||||||
while (number++ < n && (result = re.exec(string)))
|
|
||||||
{
|
|
||||||
if (prevLastIndex === re.lastIndex) break;
|
|
||||||
var i = result.length - 1;
|
|
||||||
var subs = new Array(i);
|
|
||||||
while (i > 0)
|
|
||||||
{
|
|
||||||
var submatch = result[i];
|
|
||||||
subs[--i] = submatch === undefined
|
|
||||||
? _elm_lang$core$Maybe$Nothing
|
|
||||||
: _elm_lang$core$Maybe$Just(submatch);
|
|
||||||
}
|
|
||||||
out.push({
|
|
||||||
match: result[0],
|
|
||||||
submatches: _elm_lang$core$Native_List.fromArray(subs),
|
|
||||||
index: result.index,
|
|
||||||
number: number
|
|
||||||
});
|
|
||||||
prevLastIndex = re.lastIndex;
|
|
||||||
}
|
|
||||||
re.lastIndex = lastIndex;
|
|
||||||
return _elm_lang$core$Native_List.fromArray(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
function replace(n, re, replacer, string)
|
|
||||||
{
|
|
||||||
n = n.ctor === 'All' ? Infinity : n._0;
|
|
||||||
var count = 0;
|
|
||||||
function jsReplacer(match)
|
|
||||||
{
|
|
||||||
if (count++ >= n)
|
|
||||||
{
|
|
||||||
return match;
|
|
||||||
}
|
|
||||||
var i = arguments.length - 3;
|
|
||||||
var submatches = new Array(i);
|
|
||||||
while (i > 0)
|
|
||||||
{
|
|
||||||
var submatch = arguments[i];
|
|
||||||
submatches[--i] = submatch === undefined
|
|
||||||
? _elm_lang$core$Maybe$Nothing
|
|
||||||
: _elm_lang$core$Maybe$Just(submatch);
|
|
||||||
}
|
|
||||||
return replacer({
|
|
||||||
match: match,
|
|
||||||
submatches: _elm_lang$core$Native_List.fromArray(submatches),
|
|
||||||
index: arguments[arguments.length - 2],
|
|
||||||
number: count
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return string.replace(re, jsReplacer);
|
|
||||||
}
|
|
||||||
|
|
||||||
function split(n, re, str)
|
|
||||||
{
|
|
||||||
n = n.ctor === 'All' ? Infinity : n._0;
|
|
||||||
if (n === Infinity)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.fromArray(str.split(re));
|
|
||||||
}
|
|
||||||
var string = str;
|
|
||||||
var result;
|
|
||||||
var out = [];
|
|
||||||
var start = re.lastIndex;
|
|
||||||
var restoreLastIndex = re.lastIndex;
|
|
||||||
while (n--)
|
|
||||||
{
|
|
||||||
if (!(result = re.exec(string))) break;
|
|
||||||
out.push(string.slice(start, result.index));
|
|
||||||
start = re.lastIndex;
|
|
||||||
}
|
|
||||||
out.push(string.slice(start));
|
|
||||||
re.lastIndex = restoreLastIndex;
|
|
||||||
return _elm_lang$core$Native_List.fromArray(out);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
regex: regex,
|
|
||||||
caseInsensitive: caseInsensitive,
|
|
||||||
escape: escape,
|
|
||||||
|
|
||||||
contains: F2(contains),
|
|
||||||
find: F3(find),
|
|
||||||
replace: F4(replace),
|
|
||||||
split: F3(split)
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,281 +0,0 @@
|
|||||||
//import Native.Utils //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Scheduler = function() {
|
|
||||||
|
|
||||||
var MAX_STEPS = 10000;
|
|
||||||
|
|
||||||
|
|
||||||
// TASKS
|
|
||||||
|
|
||||||
function succeed(value)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Task_succeed',
|
|
||||||
value: value
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function fail(error)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Task_fail',
|
|
||||||
value: error
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function nativeBinding(callback)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Task_nativeBinding',
|
|
||||||
callback: callback,
|
|
||||||
cancel: null
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function andThen(callback, task)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Task_andThen',
|
|
||||||
callback: callback,
|
|
||||||
task: task
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function onError(callback, task)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Task_onError',
|
|
||||||
callback: callback,
|
|
||||||
task: task
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function receive(callback)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Task_receive',
|
|
||||||
callback: callback
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// PROCESSES
|
|
||||||
|
|
||||||
function rawSpawn(task)
|
|
||||||
{
|
|
||||||
var process = {
|
|
||||||
ctor: '_Process',
|
|
||||||
id: _elm_lang$core$Native_Utils.guid(),
|
|
||||||
root: task,
|
|
||||||
stack: null,
|
|
||||||
mailbox: []
|
|
||||||
};
|
|
||||||
|
|
||||||
enqueue(process);
|
|
||||||
|
|
||||||
return process;
|
|
||||||
}
|
|
||||||
|
|
||||||
function spawn(task)
|
|
||||||
{
|
|
||||||
return nativeBinding(function(callback) {
|
|
||||||
var process = rawSpawn(task);
|
|
||||||
callback(succeed(process));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function rawSend(process, msg)
|
|
||||||
{
|
|
||||||
process.mailbox.push(msg);
|
|
||||||
enqueue(process);
|
|
||||||
}
|
|
||||||
|
|
||||||
function send(process, msg)
|
|
||||||
{
|
|
||||||
return nativeBinding(function(callback) {
|
|
||||||
rawSend(process, msg);
|
|
||||||
callback(succeed(_elm_lang$core$Native_Utils.Tuple0));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function kill(process)
|
|
||||||
{
|
|
||||||
return nativeBinding(function(callback) {
|
|
||||||
var root = process.root;
|
|
||||||
if (root.ctor === '_Task_nativeBinding' && root.cancel)
|
|
||||||
{
|
|
||||||
root.cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
process.root = null;
|
|
||||||
|
|
||||||
callback(succeed(_elm_lang$core$Native_Utils.Tuple0));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
function sleep(time)
|
|
||||||
{
|
|
||||||
return nativeBinding(function(callback) {
|
|
||||||
var id = setTimeout(function() {
|
|
||||||
callback(succeed(_elm_lang$core$Native_Utils.Tuple0));
|
|
||||||
}, time);
|
|
||||||
|
|
||||||
return function() { clearTimeout(id); };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// STEP PROCESSES
|
|
||||||
|
|
||||||
function step(numSteps, process)
|
|
||||||
{
|
|
||||||
while (numSteps < MAX_STEPS)
|
|
||||||
{
|
|
||||||
var ctor = process.root.ctor;
|
|
||||||
|
|
||||||
if (ctor === '_Task_succeed')
|
|
||||||
{
|
|
||||||
while (process.stack && process.stack.ctor === '_Task_onError')
|
|
||||||
{
|
|
||||||
process.stack = process.stack.rest;
|
|
||||||
}
|
|
||||||
if (process.stack === null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
process.root = process.stack.callback(process.root.value);
|
|
||||||
process.stack = process.stack.rest;
|
|
||||||
++numSteps;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctor === '_Task_fail')
|
|
||||||
{
|
|
||||||
while (process.stack && process.stack.ctor === '_Task_andThen')
|
|
||||||
{
|
|
||||||
process.stack = process.stack.rest;
|
|
||||||
}
|
|
||||||
if (process.stack === null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
process.root = process.stack.callback(process.root.value);
|
|
||||||
process.stack = process.stack.rest;
|
|
||||||
++numSteps;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctor === '_Task_andThen')
|
|
||||||
{
|
|
||||||
process.stack = {
|
|
||||||
ctor: '_Task_andThen',
|
|
||||||
callback: process.root.callback,
|
|
||||||
rest: process.stack
|
|
||||||
};
|
|
||||||
process.root = process.root.task;
|
|
||||||
++numSteps;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctor === '_Task_onError')
|
|
||||||
{
|
|
||||||
process.stack = {
|
|
||||||
ctor: '_Task_onError',
|
|
||||||
callback: process.root.callback,
|
|
||||||
rest: process.stack
|
|
||||||
};
|
|
||||||
process.root = process.root.task;
|
|
||||||
++numSteps;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctor === '_Task_nativeBinding')
|
|
||||||
{
|
|
||||||
process.root.cancel = process.root.callback(function(newRoot) {
|
|
||||||
process.root = newRoot;
|
|
||||||
enqueue(process);
|
|
||||||
});
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctor === '_Task_receive')
|
|
||||||
{
|
|
||||||
var mailbox = process.mailbox;
|
|
||||||
if (mailbox.length === 0)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
process.root = process.root.callback(mailbox.shift());
|
|
||||||
++numSteps;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(ctor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (numSteps < MAX_STEPS)
|
|
||||||
{
|
|
||||||
return numSteps + 1;
|
|
||||||
}
|
|
||||||
enqueue(process);
|
|
||||||
|
|
||||||
return numSteps;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// WORK QUEUE
|
|
||||||
|
|
||||||
var working = false;
|
|
||||||
var workQueue = [];
|
|
||||||
|
|
||||||
function enqueue(process)
|
|
||||||
{
|
|
||||||
workQueue.push(process);
|
|
||||||
|
|
||||||
if (!working)
|
|
||||||
{
|
|
||||||
setTimeout(work, 0);
|
|
||||||
working = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function work()
|
|
||||||
{
|
|
||||||
var numSteps = 0;
|
|
||||||
var process;
|
|
||||||
while (numSteps < MAX_STEPS && (process = workQueue.shift()))
|
|
||||||
{
|
|
||||||
if (process.root)
|
|
||||||
{
|
|
||||||
numSteps = step(numSteps, process);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!process)
|
|
||||||
{
|
|
||||||
working = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
setTimeout(work, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
|
||||||
succeed: succeed,
|
|
||||||
fail: fail,
|
|
||||||
nativeBinding: nativeBinding,
|
|
||||||
andThen: F2(andThen),
|
|
||||||
onError: F2(onError),
|
|
||||||
receive: receive,
|
|
||||||
|
|
||||||
spawn: spawn,
|
|
||||||
kill: kill,
|
|
||||||
sleep: sleep,
|
|
||||||
send: F2(send),
|
|
||||||
|
|
||||||
rawSpawn: rawSpawn,
|
|
||||||
rawSend: rawSend
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,339 +0,0 @@
|
|||||||
//import Maybe, Native.List, Native.Utils, Result //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_String = function() {
|
|
||||||
|
|
||||||
function isEmpty(str)
|
|
||||||
{
|
|
||||||
return str.length === 0;
|
|
||||||
}
|
|
||||||
function cons(chr, str)
|
|
||||||
{
|
|
||||||
return chr + str;
|
|
||||||
}
|
|
||||||
function uncons(str)
|
|
||||||
{
|
|
||||||
var hd = str[0];
|
|
||||||
if (hd)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Maybe$Just(_elm_lang$core$Native_Utils.Tuple2(_elm_lang$core$Native_Utils.chr(hd), str.slice(1)));
|
|
||||||
}
|
|
||||||
return _elm_lang$core$Maybe$Nothing;
|
|
||||||
}
|
|
||||||
function append(a, b)
|
|
||||||
{
|
|
||||||
return a + b;
|
|
||||||
}
|
|
||||||
function concat(strs)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.toArray(strs).join('');
|
|
||||||
}
|
|
||||||
function length(str)
|
|
||||||
{
|
|
||||||
return str.length;
|
|
||||||
}
|
|
||||||
function map(f, str)
|
|
||||||
{
|
|
||||||
var out = str.split('');
|
|
||||||
for (var i = out.length; i--; )
|
|
||||||
{
|
|
||||||
out[i] = f(_elm_lang$core$Native_Utils.chr(out[i]));
|
|
||||||
}
|
|
||||||
return out.join('');
|
|
||||||
}
|
|
||||||
function filter(pred, str)
|
|
||||||
{
|
|
||||||
return str.split('').map(_elm_lang$core$Native_Utils.chr).filter(pred).join('');
|
|
||||||
}
|
|
||||||
function reverse(str)
|
|
||||||
{
|
|
||||||
return str.split('').reverse().join('');
|
|
||||||
}
|
|
||||||
function foldl(f, b, str)
|
|
||||||
{
|
|
||||||
var len = str.length;
|
|
||||||
for (var i = 0; i < len; ++i)
|
|
||||||
{
|
|
||||||
b = A2(f, _elm_lang$core$Native_Utils.chr(str[i]), b);
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
function foldr(f, b, str)
|
|
||||||
{
|
|
||||||
for (var i = str.length; i--; )
|
|
||||||
{
|
|
||||||
b = A2(f, _elm_lang$core$Native_Utils.chr(str[i]), b);
|
|
||||||
}
|
|
||||||
return b;
|
|
||||||
}
|
|
||||||
function split(sep, str)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.fromArray(str.split(sep));
|
|
||||||
}
|
|
||||||
function join(sep, strs)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.toArray(strs).join(sep);
|
|
||||||
}
|
|
||||||
function repeat(n, str)
|
|
||||||
{
|
|
||||||
var result = '';
|
|
||||||
while (n > 0)
|
|
||||||
{
|
|
||||||
if (n & 1)
|
|
||||||
{
|
|
||||||
result += str;
|
|
||||||
}
|
|
||||||
n >>= 1, str += str;
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
function slice(start, end, str)
|
|
||||||
{
|
|
||||||
return str.slice(start, end);
|
|
||||||
}
|
|
||||||
function left(n, str)
|
|
||||||
{
|
|
||||||
return n < 1 ? '' : str.slice(0, n);
|
|
||||||
}
|
|
||||||
function right(n, str)
|
|
||||||
{
|
|
||||||
return n < 1 ? '' : str.slice(-n);
|
|
||||||
}
|
|
||||||
function dropLeft(n, str)
|
|
||||||
{
|
|
||||||
return n < 1 ? str : str.slice(n);
|
|
||||||
}
|
|
||||||
function dropRight(n, str)
|
|
||||||
{
|
|
||||||
return n < 1 ? str : str.slice(0, -n);
|
|
||||||
}
|
|
||||||
function pad(n, chr, str)
|
|
||||||
{
|
|
||||||
var half = (n - str.length) / 2;
|
|
||||||
return repeat(Math.ceil(half), chr) + str + repeat(half | 0, chr);
|
|
||||||
}
|
|
||||||
function padRight(n, chr, str)
|
|
||||||
{
|
|
||||||
return str + repeat(n - str.length, chr);
|
|
||||||
}
|
|
||||||
function padLeft(n, chr, str)
|
|
||||||
{
|
|
||||||
return repeat(n - str.length, chr) + str;
|
|
||||||
}
|
|
||||||
|
|
||||||
function trim(str)
|
|
||||||
{
|
|
||||||
return str.trim();
|
|
||||||
}
|
|
||||||
function trimLeft(str)
|
|
||||||
{
|
|
||||||
return str.replace(/^\s+/, '');
|
|
||||||
}
|
|
||||||
function trimRight(str)
|
|
||||||
{
|
|
||||||
return str.replace(/\s+$/, '');
|
|
||||||
}
|
|
||||||
|
|
||||||
function words(str)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.fromArray(str.trim().split(/\s+/g));
|
|
||||||
}
|
|
||||||
function lines(str)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.fromArray(str.split(/\r\n|\r|\n/g));
|
|
||||||
}
|
|
||||||
|
|
||||||
function toUpper(str)
|
|
||||||
{
|
|
||||||
return str.toUpperCase();
|
|
||||||
}
|
|
||||||
function toLower(str)
|
|
||||||
{
|
|
||||||
return str.toLowerCase();
|
|
||||||
}
|
|
||||||
|
|
||||||
function any(pred, str)
|
|
||||||
{
|
|
||||||
for (var i = str.length; i--; )
|
|
||||||
{
|
|
||||||
if (pred(_elm_lang$core$Native_Utils.chr(str[i])))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
function all(pred, str)
|
|
||||||
{
|
|
||||||
for (var i = str.length; i--; )
|
|
||||||
{
|
|
||||||
if (!pred(_elm_lang$core$Native_Utils.chr(str[i])))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
function contains(sub, str)
|
|
||||||
{
|
|
||||||
return str.indexOf(sub) > -1;
|
|
||||||
}
|
|
||||||
function startsWith(sub, str)
|
|
||||||
{
|
|
||||||
return str.indexOf(sub) === 0;
|
|
||||||
}
|
|
||||||
function endsWith(sub, str)
|
|
||||||
{
|
|
||||||
return str.length >= sub.length &&
|
|
||||||
str.lastIndexOf(sub) === str.length - sub.length;
|
|
||||||
}
|
|
||||||
function indexes(sub, str)
|
|
||||||
{
|
|
||||||
var subLen = sub.length;
|
|
||||||
|
|
||||||
if (subLen < 1)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.Nil;
|
|
||||||
}
|
|
||||||
|
|
||||||
var i = 0;
|
|
||||||
var is = [];
|
|
||||||
|
|
||||||
while ((i = str.indexOf(sub, i)) > -1)
|
|
||||||
{
|
|
||||||
is.push(i);
|
|
||||||
i = i + subLen;
|
|
||||||
}
|
|
||||||
|
|
||||||
return _elm_lang$core$Native_List.fromArray(is);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function toInt(s)
|
|
||||||
{
|
|
||||||
var len = s.length;
|
|
||||||
|
|
||||||
// if empty
|
|
||||||
if (len === 0)
|
|
||||||
{
|
|
||||||
return intErr(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if hex
|
|
||||||
var c = s[0];
|
|
||||||
if (c === '0' && s[1] === 'x')
|
|
||||||
{
|
|
||||||
for (var i = 2; i < len; ++i)
|
|
||||||
{
|
|
||||||
var c = s[i];
|
|
||||||
if (('0' <= c && c <= '9') || ('A' <= c && c <= 'F') || ('a' <= c && c <= 'f'))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
return intErr(s);
|
|
||||||
}
|
|
||||||
return _elm_lang$core$Result$Ok(parseInt(s, 16));
|
|
||||||
}
|
|
||||||
|
|
||||||
// is decimal
|
|
||||||
if (c > '9' || (c < '0' && c !== '-' && c !== '+'))
|
|
||||||
{
|
|
||||||
return intErr(s);
|
|
||||||
}
|
|
||||||
for (var i = 1; i < len; ++i)
|
|
||||||
{
|
|
||||||
var c = s[i];
|
|
||||||
if (c < '0' || '9' < c)
|
|
||||||
{
|
|
||||||
return intErr(s);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _elm_lang$core$Result$Ok(parseInt(s, 10));
|
|
||||||
}
|
|
||||||
|
|
||||||
function intErr(s)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Result$Err("could not convert string '" + s + "' to an Int");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function toFloat(s)
|
|
||||||
{
|
|
||||||
// check if it is a hex, octal, or binary number
|
|
||||||
if (s.length === 0 || /[\sxbo]/.test(s))
|
|
||||||
{
|
|
||||||
return floatErr(s);
|
|
||||||
}
|
|
||||||
var n = +s;
|
|
||||||
// faster isNaN check
|
|
||||||
return n === n ? _elm_lang$core$Result$Ok(n) : floatErr(s);
|
|
||||||
}
|
|
||||||
|
|
||||||
function floatErr(s)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Result$Err("could not convert string '" + s + "' to a Float");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function toList(str)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.fromArray(str.split('').map(_elm_lang$core$Native_Utils.chr));
|
|
||||||
}
|
|
||||||
function fromList(chars)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_List.toArray(chars).join('');
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
isEmpty: isEmpty,
|
|
||||||
cons: F2(cons),
|
|
||||||
uncons: uncons,
|
|
||||||
append: F2(append),
|
|
||||||
concat: concat,
|
|
||||||
length: length,
|
|
||||||
map: F2(map),
|
|
||||||
filter: F2(filter),
|
|
||||||
reverse: reverse,
|
|
||||||
foldl: F3(foldl),
|
|
||||||
foldr: F3(foldr),
|
|
||||||
|
|
||||||
split: F2(split),
|
|
||||||
join: F2(join),
|
|
||||||
repeat: F2(repeat),
|
|
||||||
|
|
||||||
slice: F3(slice),
|
|
||||||
left: F2(left),
|
|
||||||
right: F2(right),
|
|
||||||
dropLeft: F2(dropLeft),
|
|
||||||
dropRight: F2(dropRight),
|
|
||||||
|
|
||||||
pad: F3(pad),
|
|
||||||
padLeft: F3(padLeft),
|
|
||||||
padRight: F3(padRight),
|
|
||||||
|
|
||||||
trim: trim,
|
|
||||||
trimLeft: trimLeft,
|
|
||||||
trimRight: trimRight,
|
|
||||||
|
|
||||||
words: words,
|
|
||||||
lines: lines,
|
|
||||||
|
|
||||||
toUpper: toUpper,
|
|
||||||
toLower: toLower,
|
|
||||||
|
|
||||||
any: F2(any),
|
|
||||||
all: F2(all),
|
|
||||||
|
|
||||||
contains: F2(contains),
|
|
||||||
startsWith: F2(startsWith),
|
|
||||||
endsWith: F2(endsWith),
|
|
||||||
indexes: F2(indexes),
|
|
||||||
|
|
||||||
toInt: toInt,
|
|
||||||
toFloat: toFloat,
|
|
||||||
toList: toList,
|
|
||||||
fromList: fromList
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//import Native.Scheduler //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Time = function() {
|
|
||||||
|
|
||||||
var now = _elm_lang$core$Native_Scheduler.nativeBinding(function(callback)
|
|
||||||
{
|
|
||||||
callback(_elm_lang$core$Native_Scheduler.succeed(Date.now()));
|
|
||||||
});
|
|
||||||
|
|
||||||
function setInterval_(interval, task)
|
|
||||||
{
|
|
||||||
return _elm_lang$core$Native_Scheduler.nativeBinding(function(callback)
|
|
||||||
{
|
|
||||||
var id = setInterval(function() {
|
|
||||||
_elm_lang$core$Native_Scheduler.rawSpawn(task);
|
|
||||||
}, interval);
|
|
||||||
|
|
||||||
return function() { clearInterval(id); };
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
now: now,
|
|
||||||
setInterval_: F2(setInterval_)
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,488 +0,0 @@
|
|||||||
//import //
|
|
||||||
|
|
||||||
var _elm_lang$core$Native_Utils = function() {
|
|
||||||
|
|
||||||
// COMPARISONS
|
|
||||||
|
|
||||||
function eq(x, y)
|
|
||||||
{
|
|
||||||
var stack = [];
|
|
||||||
var isEqual = eqHelp(x, y, 0, stack);
|
|
||||||
var pair;
|
|
||||||
while (isEqual && (pair = stack.pop()))
|
|
||||||
{
|
|
||||||
isEqual = eqHelp(pair.x, pair.y, 0, stack);
|
|
||||||
}
|
|
||||||
return isEqual;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
function eqHelp(x, y, depth, stack)
|
|
||||||
{
|
|
||||||
if (depth > 100)
|
|
||||||
{
|
|
||||||
stack.push({ x: x, y: y });
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x === y)
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof x !== 'object')
|
|
||||||
{
|
|
||||||
if (typeof x === 'function')
|
|
||||||
{
|
|
||||||
throw new Error(
|
|
||||||
'Trying to use `(==)` on functions. There is no way to know if functions are "the same" in the Elm sense.'
|
|
||||||
+ ' Read more about this at http://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.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x === null || y === null)
|
|
||||||
{
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x instanceof Date)
|
|
||||||
{
|
|
||||||
return x.getTime() === y.getTime();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!('ctor' in x))
|
|
||||||
{
|
|
||||||
for (var key in x)
|
|
||||||
{
|
|
||||||
if (!eqHelp(x[key], y[key], depth + 1, stack))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// convert Dicts and Sets to lists
|
|
||||||
if (x.ctor === 'RBNode_elm_builtin' || x.ctor === 'RBEmpty_elm_builtin')
|
|
||||||
{
|
|
||||||
x = _elm_lang$core$Dict$toList(x);
|
|
||||||
y = _elm_lang$core$Dict$toList(y);
|
|
||||||
}
|
|
||||||
if (x.ctor === 'Set_elm_builtin')
|
|
||||||
{
|
|
||||||
x = _elm_lang$core$Set$toList(x);
|
|
||||||
y = _elm_lang$core$Set$toList(y);
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if lists are equal without recursion
|
|
||||||
if (x.ctor === '::')
|
|
||||||
{
|
|
||||||
var a = x;
|
|
||||||
var b = y;
|
|
||||||
while (a.ctor === '::' && b.ctor === '::')
|
|
||||||
{
|
|
||||||
if (!eqHelp(a._0, b._0, depth + 1, stack))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
a = a._1;
|
|
||||||
b = b._1;
|
|
||||||
}
|
|
||||||
return a.ctor === b.ctor;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if Arrays are equal
|
|
||||||
if (x.ctor === '_Array')
|
|
||||||
{
|
|
||||||
var xs = _elm_lang$core$Native_Array.toJSArray(x);
|
|
||||||
var ys = _elm_lang$core$Native_Array.toJSArray(y);
|
|
||||||
if (xs.length !== ys.length)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for (var i = 0; i < xs.length; i++)
|
|
||||||
{
|
|
||||||
if (!eqHelp(xs[i], ys[i], depth + 1, stack))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!eqHelp(x.ctor, y.ctor, depth + 1, stack))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var key in x)
|
|
||||||
{
|
|
||||||
if (!eqHelp(x[key], y[key], depth + 1, stack))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Code in Generate/JavaScript.hs, Basics.js, and List.js depends on
|
|
||||||
// the particular integer values assigned to LT, EQ, and GT.
|
|
||||||
|
|
||||||
var LT = -1, EQ = 0, GT = 1;
|
|
||||||
|
|
||||||
function cmp(x, y)
|
|
||||||
{
|
|
||||||
if (typeof x !== 'object')
|
|
||||||
{
|
|
||||||
return x === y ? EQ : x < y ? LT : GT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x instanceof String)
|
|
||||||
{
|
|
||||||
var a = x.valueOf();
|
|
||||||
var b = y.valueOf();
|
|
||||||
return a === b ? EQ : a < b ? LT : GT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x.ctor === '::' || x.ctor === '[]')
|
|
||||||
{
|
|
||||||
while (x.ctor === '::' && y.ctor === '::')
|
|
||||||
{
|
|
||||||
var ord = cmp(x._0, y._0);
|
|
||||||
if (ord !== EQ)
|
|
||||||
{
|
|
||||||
return ord;
|
|
||||||
}
|
|
||||||
x = x._1;
|
|
||||||
y = y._1;
|
|
||||||
}
|
|
||||||
return x.ctor === y.ctor ? EQ : x.ctor === '[]' ? LT : GT;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (x.ctor.slice(0, 6) === '_Tuple')
|
|
||||||
{
|
|
||||||
var ord;
|
|
||||||
var n = x.ctor.slice(6) - 0;
|
|
||||||
var err = 'cannot compare tuples with more than 6 elements.';
|
|
||||||
if (n === 0) return EQ;
|
|
||||||
if (n >= 1) { ord = cmp(x._0, y._0); if (ord !== EQ) return ord;
|
|
||||||
if (n >= 2) { ord = cmp(x._1, y._1); if (ord !== EQ) return ord;
|
|
||||||
if (n >= 3) { ord = cmp(x._2, y._2); if (ord !== EQ) return ord;
|
|
||||||
if (n >= 4) { ord = cmp(x._3, y._3); if (ord !== EQ) return ord;
|
|
||||||
if (n >= 5) { ord = cmp(x._4, y._4); if (ord !== EQ) return ord;
|
|
||||||
if (n >= 6) { ord = cmp(x._5, y._5); if (ord !== EQ) return ord;
|
|
||||||
if (n >= 7) throw new Error('Comparison error: ' + err); } } } } } }
|
|
||||||
return EQ;
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
'Comparison error: comparison is only defined on ints, '
|
|
||||||
+ 'floats, times, chars, strings, lists of comparable values, '
|
|
||||||
+ 'and tuples of comparable values.'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// COMMON VALUES
|
|
||||||
|
|
||||||
var Tuple0 = {
|
|
||||||
ctor: '_Tuple0'
|
|
||||||
};
|
|
||||||
|
|
||||||
function Tuple2(x, y)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '_Tuple2',
|
|
||||||
_0: x,
|
|
||||||
_1: y
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function chr(c)
|
|
||||||
{
|
|
||||||
return new String(c);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// GUID
|
|
||||||
|
|
||||||
var count = 0;
|
|
||||||
function guid(_)
|
|
||||||
{
|
|
||||||
return count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// RECORDS
|
|
||||||
|
|
||||||
function update(oldRecord, updatedFields)
|
|
||||||
{
|
|
||||||
var newRecord = {};
|
|
||||||
|
|
||||||
for (var key in oldRecord)
|
|
||||||
{
|
|
||||||
newRecord[key] = oldRecord[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var key in updatedFields)
|
|
||||||
{
|
|
||||||
newRecord[key] = updatedFields[key];
|
|
||||||
}
|
|
||||||
|
|
||||||
return newRecord;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//// LIST STUFF ////
|
|
||||||
|
|
||||||
var Nil = { ctor: '[]' };
|
|
||||||
|
|
||||||
function Cons(hd, tl)
|
|
||||||
{
|
|
||||||
return {
|
|
||||||
ctor: '::',
|
|
||||||
_0: hd,
|
|
||||||
_1: tl
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function append(xs, ys)
|
|
||||||
{
|
|
||||||
// append Strings
|
|
||||||
if (typeof xs === 'string')
|
|
||||||
{
|
|
||||||
return xs + ys;
|
|
||||||
}
|
|
||||||
|
|
||||||
// append Lists
|
|
||||||
if (xs.ctor === '[]')
|
|
||||||
{
|
|
||||||
return ys;
|
|
||||||
}
|
|
||||||
var root = Cons(xs._0, Nil);
|
|
||||||
var curr = root;
|
|
||||||
xs = xs._1;
|
|
||||||
while (xs.ctor !== '[]')
|
|
||||||
{
|
|
||||||
curr._1 = Cons(xs._0, Nil);
|
|
||||||
xs = xs._1;
|
|
||||||
curr = curr._1;
|
|
||||||
}
|
|
||||||
curr._1 = ys;
|
|
||||||
return root;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// CRASHES
|
|
||||||
|
|
||||||
function crash(moduleName, region)
|
|
||||||
{
|
|
||||||
return function(message) {
|
|
||||||
throw new Error(
|
|
||||||
'Ran into a `Debug.crash` in module `' + moduleName + '` ' + regionToString(region) + '\n'
|
|
||||||
+ 'The message provided by the code author is:\n\n '
|
|
||||||
+ message
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function crashCase(moduleName, region, value)
|
|
||||||
{
|
|
||||||
return function(message) {
|
|
||||||
throw new Error(
|
|
||||||
'Ran into a `Debug.crash` in module `' + moduleName + '`\n\n'
|
|
||||||
+ 'This was caused by the `case` expression ' + regionToString(region) + '.\n'
|
|
||||||
+ 'One of the branches ended with a crash and the following value got through:\n\n ' + toString(value) + '\n\n'
|
|
||||||
+ 'The message provided by the code author is:\n\n '
|
|
||||||
+ message
|
|
||||||
);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function regionToString(region)
|
|
||||||
{
|
|
||||||
if (region.start.line == region.end.line)
|
|
||||||
{
|
|
||||||
return 'on line ' + region.start.line;
|
|
||||||
}
|
|
||||||
return 'between lines ' + region.start.line + ' and ' + region.end.line;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TO STRING
|
|
||||||
|
|
||||||
function toString(v)
|
|
||||||
{
|
|
||||||
var type = typeof v;
|
|
||||||
if (type === 'function')
|
|
||||||
{
|
|
||||||
return '<function>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'boolean')
|
|
||||||
{
|
|
||||||
return v ? 'True' : 'False';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'number')
|
|
||||||
{
|
|
||||||
return v + '';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v instanceof String)
|
|
||||||
{
|
|
||||||
return '\'' + addSlashes(v, true) + '\'';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'string')
|
|
||||||
{
|
|
||||||
return '"' + addSlashes(v, false) + '"';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v === null)
|
|
||||||
{
|
|
||||||
return 'null';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'object' && 'ctor' in v)
|
|
||||||
{
|
|
||||||
var ctorStarter = v.ctor.substring(0, 5);
|
|
||||||
|
|
||||||
if (ctorStarter === '_Tupl')
|
|
||||||
{
|
|
||||||
var output = [];
|
|
||||||
for (var k in v)
|
|
||||||
{
|
|
||||||
if (k === 'ctor') continue;
|
|
||||||
output.push(toString(v[k]));
|
|
||||||
}
|
|
||||||
return '(' + output.join(',') + ')';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ctorStarter === '_Task')
|
|
||||||
{
|
|
||||||
return '<task>'
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.ctor === '_Array')
|
|
||||||
{
|
|
||||||
var list = _elm_lang$core$Array$toList(v);
|
|
||||||
return 'Array.fromList ' + toString(list);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.ctor === '<decoder>')
|
|
||||||
{
|
|
||||||
return '<decoder>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.ctor === '_Process')
|
|
||||||
{
|
|
||||||
return '<process:' + v.id + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.ctor === '::')
|
|
||||||
{
|
|
||||||
var output = '[' + toString(v._0);
|
|
||||||
v = v._1;
|
|
||||||
while (v.ctor === '::')
|
|
||||||
{
|
|
||||||
output += ',' + toString(v._0);
|
|
||||||
v = v._1;
|
|
||||||
}
|
|
||||||
return output + ']';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.ctor === '[]')
|
|
||||||
{
|
|
||||||
return '[]';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.ctor === 'Set_elm_builtin')
|
|
||||||
{
|
|
||||||
return 'Set.fromList ' + toString(_elm_lang$core$Set$toList(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.ctor === 'RBNode_elm_builtin' || v.ctor === 'RBEmpty_elm_builtin')
|
|
||||||
{
|
|
||||||
return 'Dict.fromList ' + toString(_elm_lang$core$Dict$toList(v));
|
|
||||||
}
|
|
||||||
|
|
||||||
var output = '';
|
|
||||||
for (var i in v)
|
|
||||||
{
|
|
||||||
if (i === 'ctor') continue;
|
|
||||||
var str = toString(v[i]);
|
|
||||||
var c0 = str[0];
|
|
||||||
var parenless = c0 === '{' || c0 === '(' || c0 === '<' || c0 === '"' || str.indexOf(' ') < 0;
|
|
||||||
output += ' ' + (parenless ? str : '(' + str + ')');
|
|
||||||
}
|
|
||||||
return v.ctor + output;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (type === 'object')
|
|
||||||
{
|
|
||||||
if (v instanceof Date)
|
|
||||||
{
|
|
||||||
return '<' + v.toString() + '>';
|
|
||||||
}
|
|
||||||
|
|
||||||
if (v.elm_web_socket)
|
|
||||||
{
|
|
||||||
return '<websocket>';
|
|
||||||
}
|
|
||||||
|
|
||||||
var output = [];
|
|
||||||
for (var k in v)
|
|
||||||
{
|
|
||||||
output.push(k + ' = ' + toString(v[k]));
|
|
||||||
}
|
|
||||||
if (output.length === 0)
|
|
||||||
{
|
|
||||||
return '{}';
|
|
||||||
}
|
|
||||||
return '{ ' + output.join(', ') + ' }';
|
|
||||||
}
|
|
||||||
|
|
||||||
return '<internal structure>';
|
|
||||||
}
|
|
||||||
|
|
||||||
function addSlashes(str, isChar)
|
|
||||||
{
|
|
||||||
var s = str.replace(/\\/g, '\\\\')
|
|
||||||
.replace(/\n/g, '\\n')
|
|
||||||
.replace(/\t/g, '\\t')
|
|
||||||
.replace(/\r/g, '\\r')
|
|
||||||
.replace(/\v/g, '\\v')
|
|
||||||
.replace(/\0/g, '\\0');
|
|
||||||
if (isChar)
|
|
||||||
{
|
|
||||||
return s.replace(/\'/g, '\\\'');
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return s.replace(/\"/g, '\\"');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return {
|
|
||||||
eq: eq,
|
|
||||||
cmp: cmp,
|
|
||||||
Tuple0: Tuple0,
|
|
||||||
Tuple2: Tuple2,
|
|
||||||
chr: chr,
|
|
||||||
update: update,
|
|
||||||
guid: guid,
|
|
||||||
|
|
||||||
append: F2(append),
|
|
||||||
|
|
||||||
crash: crash,
|
|
||||||
crashCase: crashCase,
|
|
||||||
|
|
||||||
toString: toString
|
|
||||||
};
|
|
||||||
|
|
||||||
}();
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
module Platform exposing
|
|
||||||
( Program, program, programWithFlags
|
|
||||||
, Task, ProcessId
|
|
||||||
, Router, sendToApp, sendToSelf
|
|
||||||
)
|
|
||||||
|
|
||||||
{-|
|
|
||||||
|
|
||||||
# Programs
|
|
||||||
@docs Program, program, programWithFlags
|
|
||||||
|
|
||||||
# Platform Internals
|
|
||||||
|
|
||||||
## Tasks and Processes
|
|
||||||
@docs Task, ProcessId
|
|
||||||
|
|
||||||
## Effect Manager Helpers
|
|
||||||
|
|
||||||
An extremely tiny portion of library authors should ever write effect managers.
|
|
||||||
Fundamentally, Elm needs maybe 10 of them total. I get that people are smart,
|
|
||||||
curious, etc. but that is not a substitute for a legitimate reason to make an
|
|
||||||
effect manager. Do you have an *organic need* this fills? Or are you just
|
|
||||||
curious? Public discussions of your explorations should be framed accordingly.
|
|
||||||
|
|
||||||
@docs Router, sendToApp, sendToSelf
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Basics exposing (Never)
|
|
||||||
import Native.Platform
|
|
||||||
import Native.Scheduler
|
|
||||||
import Platform.Cmd exposing (Cmd)
|
|
||||||
import Platform.Sub exposing (Sub)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- PROGRAMS
|
|
||||||
|
|
||||||
|
|
||||||
{-| A `Program` describes how to manage your Elm app.
|
|
||||||
|
|
||||||
You can create [headless][] programs with the [`program`](#program) and
|
|
||||||
[`programWithFlags`](#programWithFlags) functions. Similar functions exist in
|
|
||||||
[`Html`][html] that let you specify a view.
|
|
||||||
|
|
||||||
[headless]: https://en.wikipedia.org/wiki/Headless_software
|
|
||||||
[html]: http://package.elm-lang.org/packages/elm-lang/html/latest/Html
|
|
||||||
|
|
||||||
Honestly, it is totally normal if this seems crazy at first. The best way to
|
|
||||||
understand is to work through [guide.elm-lang.org](http://guide.elm-lang.org/).
|
|
||||||
It makes way more sense in context!
|
|
||||||
-}
|
|
||||||
type Program flags model msg = Program
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a [headless][] program. This is great if you want to use Elm as the
|
|
||||||
“brain” for something else. You can still communicate with JS via
|
|
||||||
ports and manage your model, you just do not have to specify a `view`.
|
|
||||||
|
|
||||||
[headless]: https://en.wikipedia.org/wiki/Headless_software
|
|
||||||
|
|
||||||
Initializing a headless program from JavaScript looks like this:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var app = Elm.MyThing.worker();
|
|
||||||
```
|
|
||||||
-}
|
|
||||||
program
|
|
||||||
: { init : (model, Cmd msg)
|
|
||||||
, update : msg -> model -> (model, Cmd msg)
|
|
||||||
, subscriptions : model -> Sub msg
|
|
||||||
}
|
|
||||||
-> Program Never model msg
|
|
||||||
program =
|
|
||||||
Native.Platform.program
|
|
||||||
|
|
||||||
|
|
||||||
{-| Same as [`program`](#program), but you can provide flags. Initializing a
|
|
||||||
headless program (with flags) from JavaScript looks like this:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
var app = Elm.MyThing.worker({ user: 'Tom', token: 1234 });
|
|
||||||
```
|
|
||||||
|
|
||||||
Whatever argument you provide to `worker` will get converted to an Elm value,
|
|
||||||
allowing you to configure your Elm program however you want from JavaScript!
|
|
||||||
-}
|
|
||||||
programWithFlags
|
|
||||||
: { init : flags -> (model, Cmd msg)
|
|
||||||
, update : msg -> model -> (model, Cmd msg)
|
|
||||||
, subscriptions : model -> Sub msg
|
|
||||||
}
|
|
||||||
-> Program flags model msg
|
|
||||||
programWithFlags =
|
|
||||||
Native.Platform.programWithFlags
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- TASKS and PROCESSES
|
|
||||||
|
|
||||||
{-| Head over to the documentation for the [`Task`](Task) module for more
|
|
||||||
information on this. It is only defined here because it is a platform
|
|
||||||
primitive.
|
|
||||||
-}
|
|
||||||
type Task err ok = Task
|
|
||||||
|
|
||||||
|
|
||||||
{-| Head over to the documentation for the [`Process`](Process) module for
|
|
||||||
information on this. It is only defined here because it is a platform
|
|
||||||
primitive.
|
|
||||||
-}
|
|
||||||
type ProcessId = ProcessId
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- EFFECT MANAGER INTERNALS
|
|
||||||
|
|
||||||
|
|
||||||
{-| An effect manager has access to a “router” that routes messages between
|
|
||||||
the main app and your individual effect manager.
|
|
||||||
-}
|
|
||||||
type Router appMsg selfMsg =
|
|
||||||
Router
|
|
||||||
|
|
||||||
|
|
||||||
{-| Send the router a message for the main loop of your app. This message will
|
|
||||||
be handled by the overall `update` function, just like events from `Html`.
|
|
||||||
-}
|
|
||||||
sendToApp : Router msg a -> msg -> Task x ()
|
|
||||||
sendToApp =
|
|
||||||
Native.Platform.sendToApp
|
|
||||||
|
|
||||||
|
|
||||||
{-| Send the router a message for your effect manager. This message will
|
|
||||||
be routed to the `onSelfMsg` function, where you can update the state of your
|
|
||||||
effect manager as necessary.
|
|
||||||
|
|
||||||
As an example, the effect manager for web sockets
|
|
||||||
-}
|
|
||||||
sendToSelf : Router a msg -> msg -> Task x ()
|
|
||||||
sendToSelf =
|
|
||||||
Native.Platform.sendToSelf
|
|
||||||
|
|
||||||
|
|
||||||
hack =
|
|
||||||
Native.Scheduler.succeed
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
module Platform.Cmd exposing
|
|
||||||
( Cmd
|
|
||||||
, map
|
|
||||||
, batch
|
|
||||||
, none
|
|
||||||
, (!)
|
|
||||||
)
|
|
||||||
|
|
||||||
{-|
|
|
||||||
|
|
||||||
# Effects
|
|
||||||
|
|
||||||
Elm has **managed effects**, meaning that things like HTTP requests or writing
|
|
||||||
to disk are all treated as *data* in Elm. When this data is given to the Elm
|
|
||||||
runtime system, it can do some “query optimization” before actually performing
|
|
||||||
the effect. Perhaps unexpectedly, this managed effects idea is the heart of why
|
|
||||||
Elm is so nice for testing, reuse, reproducibility, etc.
|
|
||||||
|
|
||||||
There are two kinds of managed effects you will use in your programs: commands
|
|
||||||
and subscriptions.
|
|
||||||
|
|
||||||
@docs Cmd, map, batch, none, (!)
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Platform
|
|
||||||
|
|
||||||
|
|
||||||
{-| A command is a way of telling Elm, “Hey, I want you to do this thing!”
|
|
||||||
So if you want to send an HTTP request, you would need to command Elm to do it.
|
|
||||||
Or if you wanted to ask for geolocation, you would need to command Elm to go
|
|
||||||
get it.
|
|
||||||
|
|
||||||
Every `Cmd` specifies (1) which effects you need access to and (2) the type of
|
|
||||||
messages that will come back into your application.
|
|
||||||
|
|
||||||
**Note:** Do not worry if this seems confusing at first! As with every Elm user
|
|
||||||
ever, commands will make more sense as you work through [the Elm Architecture
|
|
||||||
Tutorial](http://guide.elm-lang.org/architecture/index.html) and see how they
|
|
||||||
fit into a real application!
|
|
||||||
-}
|
|
||||||
type Cmd msg = Cmd
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map : (a -> msg) -> Cmd a -> Cmd msg
|
|
||||||
map =
|
|
||||||
Native.Platform.map
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
batch : List (Cmd msg) -> Cmd msg
|
|
||||||
batch =
|
|
||||||
Native.Platform.batch
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
none : Cmd msg
|
|
||||||
none =
|
|
||||||
batch []
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
(!) : model -> List (Cmd msg) -> (model, Cmd msg)
|
|
||||||
(!) model commands =
|
|
||||||
(model, batch commands)
|
|
||||||
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
module Platform.Sub exposing
|
|
||||||
( Sub
|
|
||||||
, map
|
|
||||||
, batch
|
|
||||||
, none
|
|
||||||
)
|
|
||||||
|
|
||||||
{-|
|
|
||||||
|
|
||||||
@docs Sub, map, batch, none
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Platform
|
|
||||||
|
|
||||||
|
|
||||||
{-| A subscription is a way of telling Elm, “Hey, let me know if anything
|
|
||||||
interesting happens over there!” So if you want to listen for messages on a web
|
|
||||||
socket, you would tell Elm to create a subscription. If you want to get clock
|
|
||||||
ticks, you would tell Elm to subscribe to that. The cool thing here is that
|
|
||||||
this means *Elm* manages all the details of subscriptions instead of *you*.
|
|
||||||
So if a web socket goes down, *you* do not need to manually reconnect with an
|
|
||||||
exponential backoff strategy, *Elm* does this all for you behind the scenes!
|
|
||||||
|
|
||||||
Every `Sub` specifies (1) which effects you need access to and (2) the type of
|
|
||||||
messages that will come back into your application.
|
|
||||||
|
|
||||||
**Note:** Do not worry if this seems confusing at first! As with every Elm user
|
|
||||||
ever, subscriptions will make more sense as you work through [the Elm Architecture
|
|
||||||
Tutorial](http://guide.elm-lang.org/architecture/index.html) and see how they fit
|
|
||||||
into a real application!
|
|
||||||
-}
|
|
||||||
type Sub msg = Sub
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map : (a -> msg) -> Sub a -> Sub msg
|
|
||||||
map =
|
|
||||||
Native.Platform.map
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
batch : List (Sub msg) -> Sub msg
|
|
||||||
batch =
|
|
||||||
Native.Platform.batch
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
none : Sub msg
|
|
||||||
none =
|
|
||||||
batch []
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
module Process exposing
|
|
||||||
( Id
|
|
||||||
, spawn
|
|
||||||
, sleep
|
|
||||||
, kill
|
|
||||||
)
|
|
||||||
|
|
||||||
{-|
|
|
||||||
|
|
||||||
# Processes
|
|
||||||
@docs Id, spawn, sleep, kill
|
|
||||||
|
|
||||||
## Future Plans
|
|
||||||
|
|
||||||
Right now, this library is pretty sparse. For example, there is no public API
|
|
||||||
for processes to communicate with each other. This is a really important
|
|
||||||
ability, but it is also something that is extraordinarily easy to get wrong!
|
|
||||||
|
|
||||||
I think the trend will be towards an Erlang style of concurrency, where every
|
|
||||||
process has an “event queue” that anyone can send messages to. I currently
|
|
||||||
think the API will be extended to be more like this:
|
|
||||||
|
|
||||||
type Id exit msg
|
|
||||||
|
|
||||||
spawn : Task exit a -> Task x (Id exit Never)
|
|
||||||
|
|
||||||
kill : Id exit msg -> Task x ()
|
|
||||||
|
|
||||||
send : Id exit msg -> msg -> Task x ()
|
|
||||||
|
|
||||||
A process `Id` will have two type variables to make sure all communication is
|
|
||||||
valid. The `exit` type describes the messages that are produced if the process
|
|
||||||
fails because of user code. So if processes are linked and trapping errors,
|
|
||||||
they will need to handle this. The `msg` type just describes what kind of
|
|
||||||
messages this process can be sent by strangers.
|
|
||||||
|
|
||||||
We shall see though! This is just a draft that does not cover nearly everything
|
|
||||||
it needs to, so the long-term vision for concurrency in Elm will be rolling out
|
|
||||||
slowly as I get more data and experience.
|
|
||||||
|
|
||||||
I ask that people bullish on compiling to node.js keep this in mind. I think we
|
|
||||||
can do better than the hopelessly bad concurrency model of node.js, and I hope
|
|
||||||
the Elm community will be supportive of being more ambitious, even if it takes
|
|
||||||
longer. That’s kind of what Elm is all about.
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Basics exposing (Never)
|
|
||||||
import Native.Scheduler
|
|
||||||
import Platform
|
|
||||||
import Task exposing (Task)
|
|
||||||
import Time exposing (Time)
|
|
||||||
|
|
||||||
|
|
||||||
{-| A light-weight process that runs concurrently. You can use `spawn` to
|
|
||||||
get a bunch of different tasks running in different processes. The Elm runtime
|
|
||||||
will interleave their progress. So if a task is taking too long, we will pause
|
|
||||||
it at an `andThen` and switch over to other stuff.
|
|
||||||
|
|
||||||
**Note:** We make a distinction between *concurrency* which means interleaving
|
|
||||||
different sequences and *parallelism* which means running different
|
|
||||||
sequences at the exact same time. For example, a
|
|
||||||
[time-sharing system](https://en.wikipedia.org/wiki/Time-sharing) is definitely
|
|
||||||
concurrent, but not necessarily parallel. So even though JS runs within a
|
|
||||||
single OS-level thread, Elm can still run things concurrently.
|
|
||||||
-}
|
|
||||||
type alias Id =
|
|
||||||
Platform.ProcessId
|
|
||||||
|
|
||||||
|
|
||||||
{-| Run a task in its own light-weight process. In the following example,
|
|
||||||
`task1` and `task2` will be interleaved. If `task1` makes a long HTTP request
|
|
||||||
or is just taking a long time, we can hop over to `task2` and do some work
|
|
||||||
there.
|
|
||||||
|
|
||||||
spawn task1
|
|
||||||
|> Task.andThen (\_ -> spawn task2)
|
|
||||||
|
|
||||||
**Note:** This creates a relatively restricted kind of `Process` because it
|
|
||||||
cannot receive any messages. More flexibility for user-defined processes will
|
|
||||||
come in a later release!
|
|
||||||
-}
|
|
||||||
spawn : Task x a -> Task y Id
|
|
||||||
spawn =
|
|
||||||
Native.Scheduler.spawn
|
|
||||||
|
|
||||||
|
|
||||||
{-| Block progress on the current process for a given amount of time. The
|
|
||||||
JavaScript equivalent of this is [`setTimeout`][setTimeout] which lets you
|
|
||||||
delay work until later.
|
|
||||||
|
|
||||||
[setTimeout]: https://developer.mozilla.org/en-US/docs/Web/API/WindowTimers/setTimeout
|
|
||||||
-}
|
|
||||||
sleep : Time -> Task x ()
|
|
||||||
sleep =
|
|
||||||
Native.Scheduler.sleep
|
|
||||||
|
|
||||||
|
|
||||||
{-| Sometimes you `spawn` a process, but later decide it would be a waste to
|
|
||||||
have it keep running and doing stuff. The `kill` function will force a process
|
|
||||||
to bail on whatever task it is running. So if there is an HTTP request in
|
|
||||||
flight, it will also abort the request.
|
|
||||||
-}
|
|
||||||
kill : Id -> Task x ()
|
|
||||||
kill =
|
|
||||||
Native.Scheduler.kill
|
|
||||||
|
|
||||||
@@ -1,532 +0,0 @@
|
|||||||
effect module Random where { command = MyCmd } exposing
|
|
||||||
( Generator, Seed
|
|
||||||
, bool, int, float
|
|
||||||
, list, pair
|
|
||||||
, map, map2, map3, map4, map5
|
|
||||||
, andThen
|
|
||||||
, minInt, maxInt
|
|
||||||
, generate
|
|
||||||
, step, initialSeed
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| This library helps you generate pseudo-random values.
|
|
||||||
|
|
||||||
This library is all about building [`generators`](#Generator) for whatever
|
|
||||||
type of values you need. There are a bunch of primitive generators like
|
|
||||||
[`bool`](#bool) and [`int`](#int) that you can build up into fancier
|
|
||||||
generators with functions like [`list`](#list) and [`map`](#map).
|
|
||||||
|
|
||||||
It may be helpful to [read about JSON decoders][json] because they work very
|
|
||||||
similarly.
|
|
||||||
|
|
||||||
[json]: https://evancz.gitbooks.io/an-introduction-to-elm/content/interop/json.html
|
|
||||||
|
|
||||||
> *Note:* This is an implementation of the Portable Combined Generator of
|
|
||||||
L'Ecuyer for 32-bit computers. It is almost a direct translation from the
|
|
||||||
[System.Random](http://hackage.haskell.org/package/random-1.0.1.1/docs/System-Random.html)
|
|
||||||
module. It has a period of roughly 2.30584e18.
|
|
||||||
|
|
||||||
# Generators
|
|
||||||
@docs Generator
|
|
||||||
|
|
||||||
# Primitive Generators
|
|
||||||
@docs bool, int, float
|
|
||||||
|
|
||||||
# Data Structure Generators
|
|
||||||
@docs pair, list
|
|
||||||
|
|
||||||
# Custom Generators
|
|
||||||
@docs map, map2, map3, map4, map5, andThen
|
|
||||||
|
|
||||||
# Generate Values
|
|
||||||
@docs generate
|
|
||||||
|
|
||||||
# Generate Values Manually
|
|
||||||
@docs step, Seed, initialSeed
|
|
||||||
|
|
||||||
# Constants
|
|
||||||
@docs maxInt, minInt
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import List exposing ((::))
|
|
||||||
import Platform
|
|
||||||
import Platform.Cmd exposing (Cmd)
|
|
||||||
import Task exposing (Task)
|
|
||||||
import Time
|
|
||||||
import Tuple
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- PRIMITIVE GENERATORS
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a generator that produces boolean values. The following example
|
|
||||||
simulates a coin flip that may land heads or tails.
|
|
||||||
|
|
||||||
type Flip = Heads | Tails
|
|
||||||
|
|
||||||
coinFlip : Generator Flip
|
|
||||||
coinFlip =
|
|
||||||
map (\b -> if b then Heads else Tails) bool
|
|
||||||
-}
|
|
||||||
bool : Generator Bool
|
|
||||||
bool =
|
|
||||||
map ((==) 1) (int 0 1)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Generate 32-bit integers in a given range.
|
|
||||||
|
|
||||||
int 0 10 -- an integer between zero and ten
|
|
||||||
int -5 5 -- an integer between -5 and 5
|
|
||||||
|
|
||||||
int minInt maxInt -- an integer in the widest range feasible
|
|
||||||
|
|
||||||
This function *can* produce values outside of the range [[`minInt`](#minInt),
|
|
||||||
[`maxInt`](#maxInt)] but sufficient randomness is not guaranteed.
|
|
||||||
-}
|
|
||||||
int : Int -> Int -> Generator Int
|
|
||||||
int a b =
|
|
||||||
Generator <| \(Seed seed) ->
|
|
||||||
let
|
|
||||||
(lo,hi) =
|
|
||||||
if a < b then (a,b) else (b,a)
|
|
||||||
|
|
||||||
k = hi - lo + 1
|
|
||||||
-- 2^31 - 87
|
|
||||||
base = 2147483561
|
|
||||||
n = iLogBase base k
|
|
||||||
|
|
||||||
f n acc state =
|
|
||||||
case n of
|
|
||||||
0 -> (acc, state)
|
|
||||||
_ ->
|
|
||||||
let
|
|
||||||
(x, nextState) = seed.next state
|
|
||||||
in
|
|
||||||
f (n - 1) (x + acc * base) nextState
|
|
||||||
|
|
||||||
(v, nextState) =
|
|
||||||
f n 1 seed.state
|
|
||||||
in
|
|
||||||
( lo + v % k
|
|
||||||
, Seed { seed | state = nextState }
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
iLogBase : Int -> Int -> Int
|
|
||||||
iLogBase b i =
|
|
||||||
if i < b then
|
|
||||||
1
|
|
||||||
else
|
|
||||||
1 + iLogBase b (i // b)
|
|
||||||
|
|
||||||
|
|
||||||
{-| The maximum value for randomly generated 32-bit ints: 2147483647 -}
|
|
||||||
maxInt : Int
|
|
||||||
maxInt =
|
|
||||||
2147483647
|
|
||||||
|
|
||||||
|
|
||||||
{-| The minimum value for randomly generated 32-bit ints: -2147483648 -}
|
|
||||||
minInt : Int
|
|
||||||
minInt =
|
|
||||||
-2147483648
|
|
||||||
|
|
||||||
|
|
||||||
{-| Generate floats in a given range. The following example is a generator
|
|
||||||
that produces decimals between 0 and 1.
|
|
||||||
|
|
||||||
probability : Generator Float
|
|
||||||
probability =
|
|
||||||
float 0 1
|
|
||||||
-}
|
|
||||||
float : Float -> Float -> Generator Float
|
|
||||||
float a b =
|
|
||||||
Generator <| \seed ->
|
|
||||||
let
|
|
||||||
(lo, hi) =
|
|
||||||
if a < b then (a,b) else (b,a)
|
|
||||||
|
|
||||||
(number, newSeed) =
|
|
||||||
step (int minInt maxInt) seed
|
|
||||||
|
|
||||||
negativeOneToOne =
|
|
||||||
toFloat number / toFloat (maxInt - minInt)
|
|
||||||
|
|
||||||
scaled =
|
|
||||||
(lo+hi)/2 + ((hi-lo) * negativeOneToOne)
|
|
||||||
in
|
|
||||||
(scaled, newSeed)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- DATA STRUCTURES
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a pair of random values. A common use of this might be to generate
|
|
||||||
a point in a certain 2D space. Imagine we have a collage that is 400 pixels
|
|
||||||
wide and 200 pixels tall.
|
|
||||||
|
|
||||||
randomPoint : Generator (Int,Int)
|
|
||||||
randomPoint =
|
|
||||||
pair (int -200 200) (int -100 100)
|
|
||||||
|
|
||||||
-}
|
|
||||||
pair : Generator a -> Generator b -> Generator (a,b)
|
|
||||||
pair genA genB =
|
|
||||||
map2 (,) genA genB
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a list of random values.
|
|
||||||
|
|
||||||
floatList : Generator (List Float)
|
|
||||||
floatList =
|
|
||||||
list 10 (float 0 1)
|
|
||||||
|
|
||||||
intList : Generator (List Int)
|
|
||||||
intList =
|
|
||||||
list 5 (int 0 100)
|
|
||||||
|
|
||||||
intPairs : Generator (List (Int, Int))
|
|
||||||
intPairs =
|
|
||||||
list 10 <| pair (int 0 100) (int 0 100)
|
|
||||||
-}
|
|
||||||
list : Int -> Generator a -> Generator (List a)
|
|
||||||
list n (Generator generate) =
|
|
||||||
Generator <| \seed ->
|
|
||||||
listHelp [] n generate seed
|
|
||||||
|
|
||||||
|
|
||||||
listHelp : List a -> Int -> (Seed -> (a,Seed)) -> Seed -> (List a, Seed)
|
|
||||||
listHelp list n generate seed =
|
|
||||||
if n < 1 then
|
|
||||||
(List.reverse list, seed)
|
|
||||||
|
|
||||||
else
|
|
||||||
let
|
|
||||||
(value, newSeed) =
|
|
||||||
generate seed
|
|
||||||
in
|
|
||||||
listHelp (value :: list) (n-1) generate newSeed
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- CUSTOM GENERATORS
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform the values produced by a generator. The following examples show
|
|
||||||
how to generate booleans and letters based on a basic integer generator.
|
|
||||||
|
|
||||||
bool : Generator Bool
|
|
||||||
bool =
|
|
||||||
map ((==) 1) (int 0 1)
|
|
||||||
|
|
||||||
lowercaseLetter : Generator Char
|
|
||||||
lowercaseLetter =
|
|
||||||
map (\n -> Char.fromCode (n + 97)) (int 0 25)
|
|
||||||
|
|
||||||
uppercaseLetter : Generator Char
|
|
||||||
uppercaseLetter =
|
|
||||||
map (\n -> Char.fromCode (n + 65)) (int 0 25)
|
|
||||||
|
|
||||||
-}
|
|
||||||
map : (a -> b) -> Generator a -> Generator b
|
|
||||||
map func (Generator genA) =
|
|
||||||
Generator <| \seed0 ->
|
|
||||||
let
|
|
||||||
(a, seed1) = genA seed0
|
|
||||||
in
|
|
||||||
(func a, seed1)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Combine two generators.
|
|
||||||
|
|
||||||
This function is used to define things like [`pair`](#pair) where you want to
|
|
||||||
put two generators together.
|
|
||||||
|
|
||||||
pair : Generator a -> Generator b -> Generator (a,b)
|
|
||||||
pair genA genB =
|
|
||||||
map2 (,) genA genB
|
|
||||||
|
|
||||||
-}
|
|
||||||
map2 : (a -> b -> c) -> Generator a -> Generator b -> Generator c
|
|
||||||
map2 func (Generator genA) (Generator genB) =
|
|
||||||
Generator <| \seed0 ->
|
|
||||||
let
|
|
||||||
(a, seed1) = genA seed0
|
|
||||||
(b, seed2) = genB seed1
|
|
||||||
in
|
|
||||||
(func a b, seed2)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Combine three generators. This could be used to produce random colors.
|
|
||||||
|
|
||||||
import Color
|
|
||||||
|
|
||||||
rgb : Generator Color.Color
|
|
||||||
rgb =
|
|
||||||
map3 Color.rgb (int 0 255) (int 0 255) (int 0 255)
|
|
||||||
|
|
||||||
hsl : Generator Color.Color
|
|
||||||
hsl =
|
|
||||||
map3 Color.hsl (map degrees (int 0 360)) (float 0 1) (float 0 1)
|
|
||||||
-}
|
|
||||||
map3 : (a -> b -> c -> d) -> Generator a -> Generator b -> Generator c -> Generator d
|
|
||||||
map3 func (Generator genA) (Generator genB) (Generator genC) =
|
|
||||||
Generator <| \seed0 ->
|
|
||||||
let
|
|
||||||
(a, seed1) = genA seed0
|
|
||||||
(b, seed2) = genB seed1
|
|
||||||
(c, seed3) = genC seed2
|
|
||||||
in
|
|
||||||
(func a b c, seed3)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Combine four generators.
|
|
||||||
-}
|
|
||||||
map4 : (a -> b -> c -> d -> e) -> Generator a -> Generator b -> Generator c -> Generator d -> Generator e
|
|
||||||
map4 func (Generator genA) (Generator genB) (Generator genC) (Generator genD) =
|
|
||||||
Generator <| \seed0 ->
|
|
||||||
let
|
|
||||||
(a, seed1) = genA seed0
|
|
||||||
(b, seed2) = genB seed1
|
|
||||||
(c, seed3) = genC seed2
|
|
||||||
(d, seed4) = genD seed3
|
|
||||||
in
|
|
||||||
(func a b c d, seed4)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Combine five generators.
|
|
||||||
-}
|
|
||||||
map5 : (a -> b -> c -> d -> e -> f) -> Generator a -> Generator b -> Generator c -> Generator d -> Generator e -> Generator f
|
|
||||||
map5 func (Generator genA) (Generator genB) (Generator genC) (Generator genD) (Generator genE) =
|
|
||||||
Generator <| \seed0 ->
|
|
||||||
let
|
|
||||||
(a, seed1) = genA seed0
|
|
||||||
(b, seed2) = genB seed1
|
|
||||||
(c, seed3) = genC seed2
|
|
||||||
(d, seed4) = genD seed3
|
|
||||||
(e, seed5) = genE seed4
|
|
||||||
in
|
|
||||||
(func a b c d e, seed5)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Chain random operations, threading through the seed. In the following
|
|
||||||
example, we will generate a random letter by putting together uppercase and
|
|
||||||
lowercase letters.
|
|
||||||
|
|
||||||
letter : Generator Char
|
|
||||||
letter =
|
|
||||||
bool
|
|
||||||
|> andThen upperOrLower
|
|
||||||
|
|
||||||
upperOrLower : Bool -> Generator Char
|
|
||||||
upperOrLower b =
|
|
||||||
if b then uppercaseLetter else lowercaseLetter
|
|
||||||
|
|
||||||
-- bool : Generator Bool
|
|
||||||
-- uppercaseLetter : Generator Char
|
|
||||||
-- lowercaseLetter : Generator Char
|
|
||||||
-}
|
|
||||||
andThen : (a -> Generator b) -> Generator a -> Generator b
|
|
||||||
andThen callback (Generator generate) =
|
|
||||||
Generator <| \seed ->
|
|
||||||
let
|
|
||||||
(result, newSeed) =
|
|
||||||
generate seed
|
|
||||||
|
|
||||||
(Generator genB) =
|
|
||||||
callback result
|
|
||||||
in
|
|
||||||
genB newSeed
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- IMPLEMENTATION
|
|
||||||
|
|
||||||
|
|
||||||
{-| A `Generator` is like a recipe for generating certain random values. So a
|
|
||||||
`Generator Int` describes how to generate integers and a `Generator String`
|
|
||||||
describes how to generate strings.
|
|
||||||
|
|
||||||
To actually *run* a generator and produce the random values, you need to use
|
|
||||||
functions like [`generate`](#generate) and [`initialSeed`](#initialSeed).
|
|
||||||
-}
|
|
||||||
type Generator a =
|
|
||||||
Generator (Seed -> (a, Seed))
|
|
||||||
|
|
||||||
|
|
||||||
type State = State Int Int
|
|
||||||
|
|
||||||
|
|
||||||
{-| A `Seed` is the source of randomness in this whole system. Whenever
|
|
||||||
you want to use a generator, you need to pair it with a seed.
|
|
||||||
-}
|
|
||||||
type Seed =
|
|
||||||
Seed
|
|
||||||
{ state : State
|
|
||||||
, next : State -> (Int, State)
|
|
||||||
, split : State -> (State, State)
|
|
||||||
, range : State -> (Int,Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{-| Generate a random value as specified by a given `Generator`.
|
|
||||||
|
|
||||||
In the following example, we are trying to generate a number between 0 and 100
|
|
||||||
with the `int 0 100` generator. Each time we call `step` we need to provide a
|
|
||||||
seed. This will produce a random number and a *new* seed to use if we want to
|
|
||||||
run other generators later.
|
|
||||||
|
|
||||||
So here it is done right, where we get a new seed from each `step` call and
|
|
||||||
thread that through.
|
|
||||||
|
|
||||||
seed0 = initialSeed 31415
|
|
||||||
|
|
||||||
-- step (int 0 100) seed0 ==> (42, seed1)
|
|
||||||
-- step (int 0 100) seed1 ==> (31, seed2)
|
|
||||||
-- step (int 0 100) seed2 ==> (99, seed3)
|
|
||||||
|
|
||||||
Notice that we use different seeds on each line. This is important! If you use
|
|
||||||
the same seed, you get the same results.
|
|
||||||
|
|
||||||
-- step (int 0 100) seed0 ==> (42, seed1)
|
|
||||||
-- step (int 0 100) seed0 ==> (42, seed1)
|
|
||||||
-- step (int 0 100) seed0 ==> (42, seed1)
|
|
||||||
-}
|
|
||||||
step : Generator a -> Seed -> (a, Seed)
|
|
||||||
step (Generator generator) seed =
|
|
||||||
generator seed
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a “seed” of randomness which makes it possible to
|
|
||||||
generate random values. If you use the same seed many times, it will result
|
|
||||||
in the same thing every time! A good way to get an unexpected seed is to use
|
|
||||||
the current time.
|
|
||||||
-}
|
|
||||||
initialSeed : Int -> Seed
|
|
||||||
initialSeed n =
|
|
||||||
Seed
|
|
||||||
{ state = initState n
|
|
||||||
, next = next
|
|
||||||
, split = split
|
|
||||||
, range = range
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{-| Produce the initial generator state. Distinct arguments should be likely
|
|
||||||
to produce distinct generator states.
|
|
||||||
-}
|
|
||||||
initState : Int -> State
|
|
||||||
initState seed =
|
|
||||||
let
|
|
||||||
s = max seed -seed
|
|
||||||
q = s // (magicNum6-1)
|
|
||||||
s1 = s % (magicNum6-1)
|
|
||||||
s2 = q % (magicNum7-1)
|
|
||||||
in
|
|
||||||
State (s1+1) (s2+1)
|
|
||||||
|
|
||||||
|
|
||||||
magicNum0 = 40014
|
|
||||||
magicNum1 = 53668
|
|
||||||
magicNum2 = 12211
|
|
||||||
magicNum3 = 52774
|
|
||||||
magicNum4 = 40692
|
|
||||||
magicNum5 = 3791
|
|
||||||
magicNum6 = 2147483563
|
|
||||||
magicNum7 = 2147483399
|
|
||||||
magicNum8 = 2147483562
|
|
||||||
|
|
||||||
|
|
||||||
next : State -> (Int, State)
|
|
||||||
next (State state1 state2) =
|
|
||||||
-- Div always rounds down and so random numbers are biased
|
|
||||||
-- ideally we would use division that rounds towards zero so
|
|
||||||
-- that in the negative case it rounds up and in the positive case
|
|
||||||
-- it rounds down. Thus half the time it rounds up and half the time it
|
|
||||||
-- rounds down
|
|
||||||
let
|
|
||||||
k1 = state1 // magicNum1
|
|
||||||
rawState1 = magicNum0 * (state1 - k1 * magicNum1) - k1 * magicNum2
|
|
||||||
newState1 = if rawState1 < 0 then rawState1 + magicNum6 else rawState1
|
|
||||||
k2 = state2 // magicNum3
|
|
||||||
rawState2 = magicNum4 * (state2 - k2 * magicNum3) - k2 * magicNum5
|
|
||||||
newState2 = if rawState2 < 0 then rawState2 + magicNum7 else rawState2
|
|
||||||
z = newState1 - newState2
|
|
||||||
newZ = if z < 1 then z + magicNum8 else z
|
|
||||||
in
|
|
||||||
(newZ, State newState1 newState2)
|
|
||||||
|
|
||||||
|
|
||||||
split : State -> (State, State)
|
|
||||||
split (State s1 s2 as std) =
|
|
||||||
let
|
|
||||||
new_s1 =
|
|
||||||
if s1 == magicNum6-1 then 1 else s1 + 1
|
|
||||||
|
|
||||||
new_s2 =
|
|
||||||
if s2 == 1 then magicNum7-1 else s2 - 1
|
|
||||||
|
|
||||||
(State t1 t2) =
|
|
||||||
Tuple.second (next std)
|
|
||||||
in
|
|
||||||
(State new_s1 t2, State t1 new_s2)
|
|
||||||
|
|
||||||
|
|
||||||
range : State -> (Int,Int)
|
|
||||||
range _ =
|
|
||||||
(0, magicNum8)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- MANAGER
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a command that will generate random values.
|
|
||||||
|
|
||||||
Read more about how to use this in your programs in [The Elm Architecture
|
|
||||||
tutorial][arch] which has a section specifically [about random values][rand].
|
|
||||||
|
|
||||||
[arch]: https://evancz.gitbooks.io/an-introduction-to-elm/content/architecture/index.html
|
|
||||||
[rand]: https://evancz.gitbooks.io/an-introduction-to-elm/content/architecture/effects/random.html
|
|
||||||
-}
|
|
||||||
generate : (a -> msg) -> Generator a -> Cmd msg
|
|
||||||
generate tagger generator =
|
|
||||||
command (Generate (map tagger generator))
|
|
||||||
|
|
||||||
|
|
||||||
type MyCmd msg = Generate (Generator msg)
|
|
||||||
|
|
||||||
|
|
||||||
cmdMap : (a -> b) -> MyCmd a -> MyCmd b
|
|
||||||
cmdMap func (Generate generator) =
|
|
||||||
Generate (map func generator)
|
|
||||||
|
|
||||||
|
|
||||||
init : Task Never Seed
|
|
||||||
init =
|
|
||||||
Time.now
|
|
||||||
|> Task.andThen (\t -> Task.succeed (initialSeed (round t)))
|
|
||||||
|
|
||||||
|
|
||||||
onEffects : Platform.Router msg Never -> List (MyCmd msg) -> Seed -> Task Never Seed
|
|
||||||
onEffects router commands seed =
|
|
||||||
case commands of
|
|
||||||
[] ->
|
|
||||||
Task.succeed seed
|
|
||||||
|
|
||||||
Generate generator :: rest ->
|
|
||||||
let
|
|
||||||
(value, newSeed) =
|
|
||||||
step generator seed
|
|
||||||
in
|
|
||||||
Platform.sendToApp router value
|
|
||||||
|> Task.andThen (\_ -> onEffects router rest newSeed)
|
|
||||||
|
|
||||||
|
|
||||||
onSelfMsg : Platform.Router msg Never -> Never -> Seed -> Task Never Seed
|
|
||||||
onSelfMsg _ _ seed =
|
|
||||||
Task.succeed seed
|
|
||||||
@@ -1,148 +0,0 @@
|
|||||||
module Regex exposing
|
|
||||||
( Regex
|
|
||||||
, regex, escape, caseInsensitive
|
|
||||||
, HowMany(..), Match
|
|
||||||
, contains, find, replace, split
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| A library for working with regular expressions. It uses [the
|
|
||||||
same kind of regular expressions accepted by JavaScript](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions).
|
|
||||||
|
|
||||||
# Create
|
|
||||||
@docs Regex, regex, escape, caseInsensitive
|
|
||||||
|
|
||||||
# Helpful Data Structures
|
|
||||||
|
|
||||||
These data structures are needed to help define functions like [`find`](#find)
|
|
||||||
and [`replace`](#replace).
|
|
||||||
|
|
||||||
@docs HowMany, Match
|
|
||||||
|
|
||||||
# Use
|
|
||||||
@docs contains, find, replace, split
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Maybe exposing (Maybe)
|
|
||||||
import Native.Regex
|
|
||||||
|
|
||||||
|
|
||||||
{-| A regular expression, describing a certain set of strings.
|
|
||||||
-}
|
|
||||||
type Regex = Regex
|
|
||||||
|
|
||||||
|
|
||||||
{-| Escape strings to be regular expressions, making all special characters
|
|
||||||
safe. So `regex (escape "^a+")` will match exactly `"^a+"` instead of a series
|
|
||||||
of `a`’s that start at the beginning of the line.
|
|
||||||
-}
|
|
||||||
escape : String -> String
|
|
||||||
escape =
|
|
||||||
Native.Regex.escape
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a Regex that matches patterns [as specified in JavaScript](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Writing_a_Regular_Expression_Pattern).
|
|
||||||
|
|
||||||
Be careful to escape backslashes properly! For example, `"\w"` is escaping the
|
|
||||||
letter `w` which is probably not what you want. You probably want `"\\w"`
|
|
||||||
instead, which escapes the backslash.
|
|
||||||
-}
|
|
||||||
regex : String -> Regex
|
|
||||||
regex =
|
|
||||||
Native.Regex.regex
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{-| Make a regex case insensitive -}
|
|
||||||
caseInsensitive : Regex -> Regex
|
|
||||||
caseInsensitive =
|
|
||||||
Native.Regex.caseInsensitive
|
|
||||||
|
|
||||||
|
|
||||||
{-| Check to see if a Regex is contained in a string.
|
|
||||||
|
|
||||||
contains (regex "123") "12345" == True
|
|
||||||
contains (regex "b+") "aabbcc" == True
|
|
||||||
|
|
||||||
contains (regex "789") "12345" == False
|
|
||||||
contains (regex "z+") "aabbcc" == False
|
|
||||||
-}
|
|
||||||
contains : Regex -> String -> Bool
|
|
||||||
contains =
|
|
||||||
Native.Regex.contains
|
|
||||||
|
|
||||||
|
|
||||||
{-| A `Match` represents all of the details about a particular match in a string.
|
|
||||||
Here are details on each field:
|
|
||||||
|
|
||||||
* `match` — the full string of the match.
|
|
||||||
* `submatches` — a regex might have [subpatterns, surrounded by
|
|
||||||
parentheses](https://developer.mozilla.org/en/docs/Web/JavaScript/Guide/Regular_Expressions#Using_Parenthesized_Substring_Matches).
|
|
||||||
If there are N subpatterns, there will be N elements in the `submatches` list.
|
|
||||||
Each submatch in this list is a `Maybe` because not all subpatterns may trigger.
|
|
||||||
For example, `(regex "(a+)|(b+)")` will either match many `a`’s or
|
|
||||||
many `b`’s, but never both.
|
|
||||||
* `index` — the index of the match in the original string.
|
|
||||||
* `number` — if you find many matches, you can think of each one
|
|
||||||
as being labeled with a `number` starting at one. So the first time you
|
|
||||||
find a match, that is match `number` one. Second time is match `number` two.
|
|
||||||
This is useful when paired with `replace All` if replacement is dependent on how
|
|
||||||
many times a pattern has appeared before.
|
|
||||||
-}
|
|
||||||
type alias Match =
|
|
||||||
{ match : String
|
|
||||||
, submatches : List (Maybe String)
|
|
||||||
, index : Int
|
|
||||||
, number : Int
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
{-| `HowMany` is used to specify how many matches you want to make. So
|
|
||||||
`replace All` would replace every match, but `replace (AtMost 2)` would
|
|
||||||
replace at most two matches (i.e. zero, one, two, but never three or more).
|
|
||||||
-}
|
|
||||||
type HowMany = All | AtMost Int
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find matches in a string:
|
|
||||||
|
|
||||||
findTwoCommas = find (AtMost 2) (regex ",")
|
|
||||||
|
|
||||||
-- map .index (findTwoCommas "a,b,c,d,e") == [1,3]
|
|
||||||
-- map .index (findTwoCommas "a b c d e") == []
|
|
||||||
|
|
||||||
places = find All (regex "[oi]n a (\\w+)") "I am on a boat in a lake."
|
|
||||||
|
|
||||||
-- map .match places == ["on a boat", "in a lake"]
|
|
||||||
-- map .submatches places == [ [Just "boat"], [Just "lake"] ]
|
|
||||||
-}
|
|
||||||
find : HowMany -> Regex -> String -> List Match
|
|
||||||
find =
|
|
||||||
Native.Regex.find
|
|
||||||
|
|
||||||
|
|
||||||
{-| Replace matches. The function from `Match` to `String` lets
|
|
||||||
you use the details of a specific match when making replacements.
|
|
||||||
|
|
||||||
devowel = replace All (regex "[aeiou]") (\_ -> "")
|
|
||||||
|
|
||||||
-- devowel "The quick brown fox" == "Th qck brwn fx"
|
|
||||||
|
|
||||||
reverseWords = replace All (regex "\\w+") (\{match} -> String.reverse match)
|
|
||||||
|
|
||||||
-- reverseWords "deliver mined parts" == "reviled denim strap"
|
|
||||||
-}
|
|
||||||
replace : HowMany -> Regex -> (Match -> String) -> String -> String
|
|
||||||
replace =
|
|
||||||
Native.Regex.replace
|
|
||||||
|
|
||||||
|
|
||||||
{-| Split a string, using the regex as the separator.
|
|
||||||
|
|
||||||
split (AtMost 1) (regex ",") "tom,99,90,85" == ["tom","99,90,85"]
|
|
||||||
|
|
||||||
split All (regex ",") "a,b,c,d" == ["a","b","c","d"]
|
|
||||||
-}
|
|
||||||
split : HowMany -> Regex -> String -> List String
|
|
||||||
split =
|
|
||||||
Native.Regex.split
|
|
||||||
@@ -1,210 +0,0 @@
|
|||||||
module Result exposing
|
|
||||||
( Result(..)
|
|
||||||
, withDefault
|
|
||||||
, map, map2, map3, map4, map5
|
|
||||||
, andThen
|
|
||||||
, toMaybe, fromMaybe, mapError
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| A `Result` is the result of a computation that may fail. This is a great
|
|
||||||
way to manage errors in Elm.
|
|
||||||
|
|
||||||
# Type and Constructors
|
|
||||||
@docs Result
|
|
||||||
|
|
||||||
# Mapping
|
|
||||||
@docs map, map2, map3, map4, map5
|
|
||||||
|
|
||||||
# Chaining
|
|
||||||
@docs andThen
|
|
||||||
|
|
||||||
# Handling Errors
|
|
||||||
@docs withDefault, toMaybe, fromMaybe, mapError
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Maybe exposing ( Maybe(Just, Nothing) )
|
|
||||||
|
|
||||||
|
|
||||||
{-| A `Result` is either `Ok` meaning the computation succeeded, or it is an
|
|
||||||
`Err` meaning that there was some failure.
|
|
||||||
-}
|
|
||||||
type Result error value
|
|
||||||
= Ok value
|
|
||||||
| Err error
|
|
||||||
|
|
||||||
|
|
||||||
{-| If the result is `Ok` return the value, but if the result is an `Err` then
|
|
||||||
return a given default value. The following examples try to parse integers.
|
|
||||||
|
|
||||||
Result.withDefault 0 (String.toInt "123") == 123
|
|
||||||
Result.withDefault 0 (String.toInt "abc") == 0
|
|
||||||
-}
|
|
||||||
withDefault : a -> Result x a -> a
|
|
||||||
withDefault def result =
|
|
||||||
case result of
|
|
||||||
Ok a ->
|
|
||||||
a
|
|
||||||
|
|
||||||
Err _ ->
|
|
||||||
def
|
|
||||||
|
|
||||||
|
|
||||||
{-| Apply a function to a result. If the result is `Ok`, it will be converted.
|
|
||||||
If the result is an `Err`, the same error value will propagate through.
|
|
||||||
|
|
||||||
map sqrt (Ok 4.0) == Ok 2.0
|
|
||||||
map sqrt (Err "bad input") == Err "bad input"
|
|
||||||
-}
|
|
||||||
map : (a -> value) -> Result x a -> Result x value
|
|
||||||
map func ra =
|
|
||||||
case ra of
|
|
||||||
Ok a -> Ok (func a)
|
|
||||||
Err e -> Err e
|
|
||||||
|
|
||||||
|
|
||||||
{-| Apply a function to two results, if both results are `Ok`. If not,
|
|
||||||
the first argument which is an `Err` will propagate through.
|
|
||||||
|
|
||||||
map2 (+) (String.toInt "1") (String.toInt "2") == Ok 3
|
|
||||||
map2 (+) (String.toInt "1") (String.toInt "y") == Err "could not convert string 'y' to an Int"
|
|
||||||
map2 (+) (String.toInt "x") (String.toInt "y") == Err "could not convert string 'x' to an Int"
|
|
||||||
-}
|
|
||||||
map2 : (a -> b -> value) -> Result x a -> Result x b -> Result x value
|
|
||||||
map2 func ra rb =
|
|
||||||
case (ra,rb) of
|
|
||||||
(Ok a, Ok b) -> Ok (func a b)
|
|
||||||
(Err x, _) -> Err x
|
|
||||||
(_, Err x) -> Err x
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map3 : (a -> b -> c -> value) -> Result x a -> Result x b -> Result x c -> Result x value
|
|
||||||
map3 func ra rb rc =
|
|
||||||
case (ra,rb,rc) of
|
|
||||||
(Ok a, Ok b, Ok c) -> Ok (func a b c)
|
|
||||||
(Err x, _, _) -> Err x
|
|
||||||
(_, Err x, _) -> Err x
|
|
||||||
(_, _, Err x) -> Err x
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map4 : (a -> b -> c -> d -> value) -> Result x a -> Result x b -> Result x c -> Result x d -> Result x value
|
|
||||||
map4 func ra rb rc rd =
|
|
||||||
case (ra,rb,rc,rd) of
|
|
||||||
(Ok a, Ok b, Ok c, Ok d) -> Ok (func a b c d)
|
|
||||||
(Err x, _, _, _) -> Err x
|
|
||||||
(_, Err x, _, _) -> Err x
|
|
||||||
(_, _, Err x, _) -> Err x
|
|
||||||
(_, _, _, Err x) -> Err x
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
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
|
|
||||||
map5 func ra rb rc rd re =
|
|
||||||
case (ra,rb,rc,rd,re) of
|
|
||||||
(Ok a, Ok b, Ok c, Ok d, Ok e) -> Ok (func a b c d e)
|
|
||||||
(Err x, _, _, _, _) -> Err x
|
|
||||||
(_, Err x, _, _, _) -> Err x
|
|
||||||
(_, _, Err x, _, _) -> Err x
|
|
||||||
(_, _, _, Err x, _) -> Err x
|
|
||||||
(_, _, _, _, Err x) -> Err x
|
|
||||||
|
|
||||||
|
|
||||||
{-| Chain together a sequence of computations that may fail. It is helpful
|
|
||||||
to see its definition:
|
|
||||||
|
|
||||||
andThen : (a -> Result e b) -> Result e a -> Result e b
|
|
||||||
andThen callback result =
|
|
||||||
case result of
|
|
||||||
Ok value -> callback value
|
|
||||||
Err msg -> Err msg
|
|
||||||
|
|
||||||
This means we only continue with the callback if things are going well. For
|
|
||||||
example, say you need to use (`toInt : String -> Result String Int`) to parse
|
|
||||||
a month and make sure it is between 1 and 12:
|
|
||||||
|
|
||||||
toValidMonth : Int -> Result String Int
|
|
||||||
toValidMonth month =
|
|
||||||
if month >= 1 && month <= 12
|
|
||||||
then Ok month
|
|
||||||
else Err "months must be between 1 and 12"
|
|
||||||
|
|
||||||
toMonth : String -> Result String Int
|
|
||||||
toMonth rawString =
|
|
||||||
toInt rawString
|
|
||||||
|> andThen toValidMonth
|
|
||||||
|
|
||||||
-- toMonth "4" == Ok 4
|
|
||||||
-- toMonth "9" == Ok 9
|
|
||||||
-- toMonth "a" == Err "cannot parse to an Int"
|
|
||||||
-- toMonth "0" == Err "months must be between 1 and 12"
|
|
||||||
|
|
||||||
This allows us to come out of a chain of operations with quite a specific error
|
|
||||||
message. It is often best to create a custom type that explicitly represents
|
|
||||||
the exact ways your computation may fail. This way it is easy to handle in your
|
|
||||||
code.
|
|
||||||
-}
|
|
||||||
andThen : (a -> Result x b) -> Result x a -> Result x b
|
|
||||||
andThen callback result =
|
|
||||||
case result of
|
|
||||||
Ok value ->
|
|
||||||
callback value
|
|
||||||
|
|
||||||
Err msg ->
|
|
||||||
Err msg
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform an `Err` value. For example, say the errors we get have too much
|
|
||||||
information:
|
|
||||||
|
|
||||||
parseInt : String -> Result ParseError Int
|
|
||||||
|
|
||||||
type alias ParseError =
|
|
||||||
{ message : String
|
|
||||||
, code : Int
|
|
||||||
, position : (Int,Int)
|
|
||||||
}
|
|
||||||
|
|
||||||
mapError .message (parseInt "123") == Ok 123
|
|
||||||
mapError .message (parseInt "abc") == Err "char 'a' is not a number"
|
|
||||||
-}
|
|
||||||
mapError : (x -> y) -> Result x a -> Result y a
|
|
||||||
mapError f result =
|
|
||||||
case result of
|
|
||||||
Ok v ->
|
|
||||||
Ok v
|
|
||||||
|
|
||||||
Err e ->
|
|
||||||
Err (f e)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert to a simpler `Maybe` if the actual error message is not needed or
|
|
||||||
you need to interact with some code that primarily uses maybes.
|
|
||||||
|
|
||||||
parseInt : String -> Result ParseError Int
|
|
||||||
|
|
||||||
maybeParseInt : String -> Maybe Int
|
|
||||||
maybeParseInt string =
|
|
||||||
toMaybe (parseInt string)
|
|
||||||
-}
|
|
||||||
toMaybe : Result x a -> Maybe a
|
|
||||||
toMaybe result =
|
|
||||||
case result of
|
|
||||||
Ok v -> Just v
|
|
||||||
Err _ -> Nothing
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert from a simple `Maybe` to interact with some code that primarily
|
|
||||||
uses `Results`.
|
|
||||||
|
|
||||||
parseInt : String -> Maybe Int
|
|
||||||
|
|
||||||
resultParseInt : String -> Result String Int
|
|
||||||
resultParseInt string =
|
|
||||||
fromMaybe ("error parsing string: " ++ toString string) (parseInt string)
|
|
||||||
-}
|
|
||||||
fromMaybe : x -> Maybe a -> Result x a
|
|
||||||
fromMaybe err maybe =
|
|
||||||
case maybe of
|
|
||||||
Just v -> Ok v
|
|
||||||
Nothing -> Err err
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
module Set exposing
|
|
||||||
( Set
|
|
||||||
, empty, singleton, insert, remove
|
|
||||||
, isEmpty, member, size
|
|
||||||
, foldl, foldr, map
|
|
||||||
, filter, partition
|
|
||||||
, union, intersect, diff
|
|
||||||
, toList, fromList
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| A set of unique values. The values can be any comparable type. This
|
|
||||||
includes `Int`, `Float`, `Time`, `Char`, `String`, and tuples or lists
|
|
||||||
of comparable types.
|
|
||||||
|
|
||||||
Insert, remove, and query operations all take *O(log n)* time.
|
|
||||||
|
|
||||||
# Sets
|
|
||||||
@docs Set
|
|
||||||
|
|
||||||
# Build
|
|
||||||
@docs empty, singleton, insert, remove
|
|
||||||
|
|
||||||
# Query
|
|
||||||
@docs isEmpty, member, size
|
|
||||||
|
|
||||||
# Combine
|
|
||||||
@docs union, intersect, diff
|
|
||||||
|
|
||||||
# Lists
|
|
||||||
@docs toList, fromList
|
|
||||||
|
|
||||||
# Transform
|
|
||||||
@docs map, foldl, foldr, filter, partition
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Basics exposing ((<|))
|
|
||||||
import Dict as Dict
|
|
||||||
import List as List
|
|
||||||
|
|
||||||
|
|
||||||
{-| Represents a set of unique values. So `(Set Int)` is a set of integers and
|
|
||||||
`(Set String)` is a set of strings.
|
|
||||||
-}
|
|
||||||
type Set t =
|
|
||||||
Set_elm_builtin (Dict.Dict t ())
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create an empty set.
|
|
||||||
-}
|
|
||||||
empty : Set a
|
|
||||||
empty =
|
|
||||||
Set_elm_builtin Dict.empty
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a set with one value.
|
|
||||||
-}
|
|
||||||
singleton : comparable -> Set comparable
|
|
||||||
singleton k =
|
|
||||||
Set_elm_builtin <| Dict.singleton k ()
|
|
||||||
|
|
||||||
|
|
||||||
{-| Insert a value into a set.
|
|
||||||
-}
|
|
||||||
insert : comparable -> Set comparable -> Set comparable
|
|
||||||
insert k (Set_elm_builtin d) =
|
|
||||||
Set_elm_builtin <| Dict.insert k () d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Remove a value from a set. If the value is not found, no changes are made.
|
|
||||||
-}
|
|
||||||
remove : comparable -> Set comparable -> Set comparable
|
|
||||||
remove k (Set_elm_builtin d) =
|
|
||||||
Set_elm_builtin <| Dict.remove k d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if a set is empty.
|
|
||||||
-}
|
|
||||||
isEmpty : Set a -> Bool
|
|
||||||
isEmpty (Set_elm_builtin d) =
|
|
||||||
Dict.isEmpty d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if a value is in a set.
|
|
||||||
-}
|
|
||||||
member : comparable -> Set comparable -> Bool
|
|
||||||
member k (Set_elm_builtin d) =
|
|
||||||
Dict.member k d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine the number of elements in a set.
|
|
||||||
-}
|
|
||||||
size : Set a -> Int
|
|
||||||
size (Set_elm_builtin d) =
|
|
||||||
Dict.size d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the union of two sets. Keep all values.
|
|
||||||
-}
|
|
||||||
union : Set comparable -> Set comparable -> Set comparable
|
|
||||||
union (Set_elm_builtin d1) (Set_elm_builtin d2) =
|
|
||||||
Set_elm_builtin <| Dict.union d1 d2
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the intersection of two sets. Keeps values that appear in both sets.
|
|
||||||
-}
|
|
||||||
intersect : Set comparable -> Set comparable -> Set comparable
|
|
||||||
intersect (Set_elm_builtin d1) (Set_elm_builtin d2) =
|
|
||||||
Set_elm_builtin <| Dict.intersect d1 d2
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the difference between the first set and the second. Keeps values
|
|
||||||
that do not appear in the second set.
|
|
||||||
-}
|
|
||||||
diff : Set comparable -> Set comparable -> Set comparable
|
|
||||||
diff (Set_elm_builtin d1) (Set_elm_builtin d2) =
|
|
||||||
Set_elm_builtin <| Dict.diff d1 d2
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a set into a list, sorted from lowest to highest.
|
|
||||||
-}
|
|
||||||
toList : Set comparable -> List comparable
|
|
||||||
toList (Set_elm_builtin d) =
|
|
||||||
Dict.keys d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a list into a set, removing any duplicates.
|
|
||||||
-}
|
|
||||||
fromList : List comparable -> Set comparable
|
|
||||||
fromList xs = List.foldl insert empty xs
|
|
||||||
|
|
||||||
|
|
||||||
{-| Fold over the values in a set, in order from lowest to highest.
|
|
||||||
-}
|
|
||||||
foldl : (comparable -> b -> b) -> b -> Set comparable -> b
|
|
||||||
foldl f b (Set_elm_builtin d) =
|
|
||||||
Dict.foldl (\k _ b -> f k b) b d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Fold over the values in a set, in order from highest to lowest.
|
|
||||||
-}
|
|
||||||
foldr : (comparable -> b -> b) -> b -> Set comparable -> b
|
|
||||||
foldr f b (Set_elm_builtin d) =
|
|
||||||
Dict.foldr (\k _ b -> f k b) b d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Map a function onto a set, creating a new set with no duplicates.
|
|
||||||
-}
|
|
||||||
map : (comparable -> comparable2) -> Set comparable -> Set comparable2
|
|
||||||
map f s = fromList (List.map f (toList s))
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a new set consisting only of elements which satisfy a predicate.
|
|
||||||
-}
|
|
||||||
filter : (comparable -> Bool) -> Set comparable -> Set comparable
|
|
||||||
filter p (Set_elm_builtin d) =
|
|
||||||
Set_elm_builtin <| Dict.filter (\k _ -> p k) d
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create two new sets; the first consisting of elements which satisfy a
|
|
||||||
predicate, the second consisting of elements which do not.
|
|
||||||
-}
|
|
||||||
partition : (comparable -> Bool) -> Set comparable -> (Set comparable, Set comparable)
|
|
||||||
partition p (Set_elm_builtin d) =
|
|
||||||
let
|
|
||||||
(p1, p2) = Dict.partition (\k _ -> p k) d
|
|
||||||
in
|
|
||||||
(Set_elm_builtin p1, Set_elm_builtin p2)
|
|
||||||
@@ -1,464 +0,0 @@
|
|||||||
module String exposing
|
|
||||||
( isEmpty, length, reverse, repeat
|
|
||||||
, cons, uncons, fromChar, append, concat, split, join, words, lines
|
|
||||||
, slice, left, right, dropLeft, dropRight
|
|
||||||
, contains, startsWith, endsWith, indexes, indices
|
|
||||||
, toInt, toFloat, toList, fromList
|
|
||||||
, toUpper, toLower, pad, padLeft, padRight, trim, trimLeft, trimRight
|
|
||||||
, map, filter, foldl, foldr, any, all
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| A built-in representation for efficient string manipulation. String literals
|
|
||||||
are enclosed in `"double quotes"`. Strings are *not* lists of characters.
|
|
||||||
|
|
||||||
# Basics
|
|
||||||
@docs isEmpty, length, reverse, repeat
|
|
||||||
|
|
||||||
# Building and Splitting
|
|
||||||
@docs cons, uncons, fromChar, append, concat, split, join, words, lines
|
|
||||||
|
|
||||||
# Get Substrings
|
|
||||||
@docs slice, left, right, dropLeft, dropRight
|
|
||||||
|
|
||||||
# Check for Substrings
|
|
||||||
@docs contains, startsWith, endsWith, indexes, indices
|
|
||||||
|
|
||||||
# Conversions
|
|
||||||
@docs toInt, toFloat, toList, fromList
|
|
||||||
|
|
||||||
# Formatting
|
|
||||||
Cosmetic operations such as padding with extra characters or trimming whitespace.
|
|
||||||
|
|
||||||
@docs toUpper, toLower,
|
|
||||||
pad, padLeft, padRight,
|
|
||||||
trim, trimLeft, trimRight
|
|
||||||
|
|
||||||
# Higher-Order Functions
|
|
||||||
@docs map, filter, foldl, foldr, any, all
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.String
|
|
||||||
import Char
|
|
||||||
import Maybe exposing (Maybe)
|
|
||||||
import Result exposing (Result)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine if a string is empty.
|
|
||||||
|
|
||||||
isEmpty "" == True
|
|
||||||
isEmpty "the world" == False
|
|
||||||
-}
|
|
||||||
isEmpty : String -> Bool
|
|
||||||
isEmpty =
|
|
||||||
Native.String.isEmpty
|
|
||||||
|
|
||||||
|
|
||||||
{-| Add a character to the beginning of a string.
|
|
||||||
|
|
||||||
cons 'T' "he truth is out there" == "The truth is out there"
|
|
||||||
-}
|
|
||||||
cons : Char -> String -> String
|
|
||||||
cons =
|
|
||||||
Native.String.cons
|
|
||||||
|
|
||||||
|
|
||||||
{-| Create a string from a given character.
|
|
||||||
|
|
||||||
fromChar 'a' == "a"
|
|
||||||
-}
|
|
||||||
fromChar : Char -> String
|
|
||||||
fromChar char =
|
|
||||||
cons char ""
|
|
||||||
|
|
||||||
|
|
||||||
{-| Split a non-empty string into its head and tail. This lets you
|
|
||||||
pattern match on strings exactly as you would with lists.
|
|
||||||
|
|
||||||
uncons "abc" == Just ('a',"bc")
|
|
||||||
uncons "" == Nothing
|
|
||||||
-}
|
|
||||||
uncons : String -> Maybe (Char, String)
|
|
||||||
uncons =
|
|
||||||
Native.String.uncons
|
|
||||||
|
|
||||||
|
|
||||||
{-| Append two strings. You can also use [the `(++)` operator](Basics#++)
|
|
||||||
to do this.
|
|
||||||
|
|
||||||
append "butter" "fly" == "butterfly"
|
|
||||||
-}
|
|
||||||
append : String -> String -> String
|
|
||||||
append =
|
|
||||||
Native.String.append
|
|
||||||
|
|
||||||
|
|
||||||
{-| Concatenate many strings into one.
|
|
||||||
|
|
||||||
concat ["never","the","less"] == "nevertheless"
|
|
||||||
-}
|
|
||||||
concat : List String -> String
|
|
||||||
concat =
|
|
||||||
Native.String.concat
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the length of a string.
|
|
||||||
|
|
||||||
length "innumerable" == 11
|
|
||||||
length "" == 0
|
|
||||||
|
|
||||||
-}
|
|
||||||
length : String -> Int
|
|
||||||
length =
|
|
||||||
Native.String.length
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform every character in a string
|
|
||||||
|
|
||||||
map (\c -> if c == '/' then '.' else c) "a/b/c" == "a.b.c"
|
|
||||||
-}
|
|
||||||
map : (Char -> Char) -> String -> String
|
|
||||||
map =
|
|
||||||
Native.String.map
|
|
||||||
|
|
||||||
|
|
||||||
{-| Keep only the characters that satisfy the predicate.
|
|
||||||
|
|
||||||
filter isDigit "R2-D2" == "22"
|
|
||||||
-}
|
|
||||||
filter : (Char -> Bool) -> String -> String
|
|
||||||
filter =
|
|
||||||
Native.String.filter
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reverse a string.
|
|
||||||
|
|
||||||
reverse "stressed" == "desserts"
|
|
||||||
-}
|
|
||||||
reverse : String -> String
|
|
||||||
reverse =
|
|
||||||
Native.String.reverse
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reduce a string from the left.
|
|
||||||
|
|
||||||
foldl cons "" "time" == "emit"
|
|
||||||
-}
|
|
||||||
foldl : (Char -> b -> b) -> b -> String -> b
|
|
||||||
foldl =
|
|
||||||
Native.String.foldl
|
|
||||||
|
|
||||||
|
|
||||||
{-| Reduce a string from the right.
|
|
||||||
|
|
||||||
foldr cons "" "time" == "time"
|
|
||||||
-}
|
|
||||||
foldr : (Char -> b -> b) -> b -> String -> b
|
|
||||||
foldr =
|
|
||||||
Native.String.foldr
|
|
||||||
|
|
||||||
|
|
||||||
{-| Split a string using a given separator.
|
|
||||||
|
|
||||||
split "," "cat,dog,cow" == ["cat","dog","cow"]
|
|
||||||
split "/" "home/evan/Desktop/" == ["home","evan","Desktop", ""]
|
|
||||||
|
|
||||||
Use [`Regex.split`](Regex#split) if you need something more flexible.
|
|
||||||
-}
|
|
||||||
split : String -> String -> List String
|
|
||||||
split =
|
|
||||||
Native.String.split
|
|
||||||
|
|
||||||
|
|
||||||
{-| Put many strings together with a given separator.
|
|
||||||
|
|
||||||
join "a" ["H","w","ii","n"] == "Hawaiian"
|
|
||||||
join " " ["cat","dog","cow"] == "cat dog cow"
|
|
||||||
join "/" ["home","evan","Desktop"] == "home/evan/Desktop"
|
|
||||||
-}
|
|
||||||
join : String -> List String -> String
|
|
||||||
join =
|
|
||||||
Native.String.join
|
|
||||||
|
|
||||||
|
|
||||||
{-| Repeat a string *n* times.
|
|
||||||
|
|
||||||
repeat 3 "ha" == "hahaha"
|
|
||||||
-}
|
|
||||||
repeat : Int -> String -> String
|
|
||||||
repeat =
|
|
||||||
Native.String.repeat
|
|
||||||
|
|
||||||
|
|
||||||
{-| Take a substring given a start and end index. Negative indexes
|
|
||||||
are taken starting from the *end* of the list.
|
|
||||||
|
|
||||||
slice 7 9 "snakes on a plane!" == "on"
|
|
||||||
slice 0 6 "snakes on a plane!" == "snakes"
|
|
||||||
slice 0 -7 "snakes on a plane!" == "snakes on a"
|
|
||||||
slice -6 -1 "snakes on a plane!" == "plane"
|
|
||||||
-}
|
|
||||||
slice : Int -> Int -> String -> String
|
|
||||||
slice =
|
|
||||||
Native.String.slice
|
|
||||||
|
|
||||||
|
|
||||||
{-| Take *n* characters from the left side of a string.
|
|
||||||
|
|
||||||
left 2 "Mulder" == "Mu"
|
|
||||||
-}
|
|
||||||
left : Int -> String -> String
|
|
||||||
left =
|
|
||||||
Native.String.left
|
|
||||||
|
|
||||||
|
|
||||||
{-| Take *n* characters from the right side of a string.
|
|
||||||
|
|
||||||
right 2 "Scully" == "ly"
|
|
||||||
-}
|
|
||||||
right : Int -> String -> String
|
|
||||||
right =
|
|
||||||
Native.String.right
|
|
||||||
|
|
||||||
|
|
||||||
{-| Drop *n* characters from the left side of a string.
|
|
||||||
|
|
||||||
dropLeft 2 "The Lone Gunmen" == "e Lone Gunmen"
|
|
||||||
-}
|
|
||||||
dropLeft : Int -> String -> String
|
|
||||||
dropLeft =
|
|
||||||
Native.String.dropLeft
|
|
||||||
|
|
||||||
|
|
||||||
{-| Drop *n* characters from the right side of a string.
|
|
||||||
|
|
||||||
dropRight 2 "Cigarette Smoking Man" == "Cigarette Smoking M"
|
|
||||||
-}
|
|
||||||
dropRight : Int -> String -> String
|
|
||||||
dropRight =
|
|
||||||
Native.String.dropRight
|
|
||||||
|
|
||||||
|
|
||||||
{-| Pad a string on both sides until it has a given length.
|
|
||||||
|
|
||||||
pad 5 ' ' "1" == " 1 "
|
|
||||||
pad 5 ' ' "11" == " 11 "
|
|
||||||
pad 5 ' ' "121" == " 121 "
|
|
||||||
-}
|
|
||||||
pad : Int -> Char -> String -> String
|
|
||||||
pad =
|
|
||||||
Native.String.pad
|
|
||||||
|
|
||||||
|
|
||||||
{-| Pad a string on the left until it has a given length.
|
|
||||||
|
|
||||||
padLeft 5 '.' "1" == "....1"
|
|
||||||
padLeft 5 '.' "11" == "...11"
|
|
||||||
padLeft 5 '.' "121" == "..121"
|
|
||||||
-}
|
|
||||||
padLeft : Int -> Char -> String -> String
|
|
||||||
padLeft =
|
|
||||||
Native.String.padLeft
|
|
||||||
|
|
||||||
|
|
||||||
{-| Pad a string on the right until it has a given length.
|
|
||||||
|
|
||||||
padRight 5 '.' "1" == "1...."
|
|
||||||
padRight 5 '.' "11" == "11..."
|
|
||||||
padRight 5 '.' "121" == "121.."
|
|
||||||
-}
|
|
||||||
padRight : Int -> Char -> String -> String
|
|
||||||
padRight =
|
|
||||||
Native.String.padRight
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get rid of whitespace on both sides of a string.
|
|
||||||
|
|
||||||
trim " hats \n" == "hats"
|
|
||||||
-}
|
|
||||||
trim : String -> String
|
|
||||||
trim =
|
|
||||||
Native.String.trim
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get rid of whitespace on the left of a string.
|
|
||||||
|
|
||||||
trimLeft " hats \n" == "hats \n"
|
|
||||||
-}
|
|
||||||
trimLeft : String -> String
|
|
||||||
trimLeft =
|
|
||||||
Native.String.trimLeft
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get rid of whitespace on the right of a string.
|
|
||||||
|
|
||||||
trimRight " hats \n" == " hats"
|
|
||||||
-}
|
|
||||||
trimRight : String -> String
|
|
||||||
trimRight =
|
|
||||||
Native.String.trimRight
|
|
||||||
|
|
||||||
|
|
||||||
{-| Break a string into words, splitting on chunks of whitespace.
|
|
||||||
|
|
||||||
words "How are \t you? \n Good?" == ["How","are","you?","Good?"]
|
|
||||||
-}
|
|
||||||
words : String -> List String
|
|
||||||
words =
|
|
||||||
Native.String.words
|
|
||||||
|
|
||||||
|
|
||||||
{-| Break a string into lines, splitting on newlines.
|
|
||||||
|
|
||||||
lines "How are you?\nGood?" == ["How are you?", "Good?"]
|
|
||||||
-}
|
|
||||||
lines : String -> List String
|
|
||||||
lines =
|
|
||||||
Native.String.lines
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a string to all upper case. Useful for case-insensitive comparisons
|
|
||||||
and VIRTUAL YELLING.
|
|
||||||
|
|
||||||
toUpper "skinner" == "SKINNER"
|
|
||||||
-}
|
|
||||||
toUpper : String -> String
|
|
||||||
toUpper =
|
|
||||||
Native.String.toUpper
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a string to all lower case. Useful for case-insensitive comparisons.
|
|
||||||
|
|
||||||
toLower "X-FILES" == "x-files"
|
|
||||||
-}
|
|
||||||
toLower : String -> String
|
|
||||||
toLower =
|
|
||||||
Native.String.toLower
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine whether *any* characters satisfy a predicate.
|
|
||||||
|
|
||||||
any isDigit "90210" == True
|
|
||||||
any isDigit "R2-D2" == True
|
|
||||||
any isDigit "heart" == False
|
|
||||||
-}
|
|
||||||
any : (Char -> Bool) -> String -> Bool
|
|
||||||
any =
|
|
||||||
Native.String.any
|
|
||||||
|
|
||||||
|
|
||||||
{-| Determine whether *all* characters satisfy a predicate.
|
|
||||||
|
|
||||||
all isDigit "90210" == True
|
|
||||||
all isDigit "R2-D2" == False
|
|
||||||
all isDigit "heart" == False
|
|
||||||
-}
|
|
||||||
all : (Char -> Bool) -> String -> Bool
|
|
||||||
all =
|
|
||||||
Native.String.all
|
|
||||||
|
|
||||||
|
|
||||||
{-| See if the second string contains the first one.
|
|
||||||
|
|
||||||
contains "the" "theory" == True
|
|
||||||
contains "hat" "theory" == False
|
|
||||||
contains "THE" "theory" == False
|
|
||||||
|
|
||||||
Use [`Regex.contains`](Regex#contains) if you need something more flexible.
|
|
||||||
-}
|
|
||||||
contains : String -> String -> Bool
|
|
||||||
contains =
|
|
||||||
Native.String.contains
|
|
||||||
|
|
||||||
|
|
||||||
{-| See if the second string starts with the first one.
|
|
||||||
|
|
||||||
startsWith "the" "theory" == True
|
|
||||||
startsWith "ory" "theory" == False
|
|
||||||
-}
|
|
||||||
startsWith : String -> String -> Bool
|
|
||||||
startsWith =
|
|
||||||
Native.String.startsWith
|
|
||||||
|
|
||||||
|
|
||||||
{-| See if the second string ends with the first one.
|
|
||||||
|
|
||||||
endsWith "the" "theory" == False
|
|
||||||
endsWith "ory" "theory" == True
|
|
||||||
-}
|
|
||||||
endsWith : String -> String -> Bool
|
|
||||||
endsWith =
|
|
||||||
Native.String.endsWith
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get all of the indexes for a substring in another string.
|
|
||||||
|
|
||||||
indexes "i" "Mississippi" == [1,4,7,10]
|
|
||||||
indexes "ss" "Mississippi" == [2,5]
|
|
||||||
indexes "needle" "haystack" == []
|
|
||||||
-}
|
|
||||||
indexes : String -> String -> List Int
|
|
||||||
indexes =
|
|
||||||
Native.String.indexes
|
|
||||||
|
|
||||||
|
|
||||||
{-| Alias for `indexes`. -}
|
|
||||||
indices : String -> String -> List Int
|
|
||||||
indices =
|
|
||||||
Native.String.indexes
|
|
||||||
|
|
||||||
|
|
||||||
{-| Try to convert a string into an int, failing on improperly formatted strings.
|
|
||||||
|
|
||||||
String.toInt "123" == Ok 123
|
|
||||||
String.toInt "-42" == Ok -42
|
|
||||||
String.toInt "3.1" == Err "could not convert string '3.1' to an Int"
|
|
||||||
String.toInt "31a" == Err "could not convert string '31a' to an Int"
|
|
||||||
|
|
||||||
If you are extracting a number from some raw user input, you will typically
|
|
||||||
want to use [`Result.withDefault`](Result#withDefault) to handle bad data:
|
|
||||||
|
|
||||||
Result.withDefault 0 (String.toInt "42") == 42
|
|
||||||
Result.withDefault 0 (String.toInt "ab") == 0
|
|
||||||
-}
|
|
||||||
toInt : String -> Result String Int
|
|
||||||
toInt =
|
|
||||||
Native.String.toInt
|
|
||||||
|
|
||||||
|
|
||||||
{-| Try to convert a string into a float, failing on improperly formatted strings.
|
|
||||||
|
|
||||||
String.toFloat "123" == Ok 123.0
|
|
||||||
String.toFloat "-42" == Ok -42.0
|
|
||||||
String.toFloat "3.1" == Ok 3.1
|
|
||||||
String.toFloat "31a" == Err "could not convert string '31a' to a Float"
|
|
||||||
|
|
||||||
If you are extracting a number from some raw user input, you will typically
|
|
||||||
want to use [`Result.withDefault`](Result#withDefault) to handle bad data:
|
|
||||||
|
|
||||||
Result.withDefault 0 (String.toFloat "42.5") == 42.5
|
|
||||||
Result.withDefault 0 (String.toFloat "cats") == 0
|
|
||||||
-}
|
|
||||||
toFloat : String -> Result String Float
|
|
||||||
toFloat =
|
|
||||||
Native.String.toFloat
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a string to a list of characters.
|
|
||||||
|
|
||||||
toList "abc" == ['a','b','c']
|
|
||||||
-}
|
|
||||||
toList : String -> List Char
|
|
||||||
toList =
|
|
||||||
Native.String.toList
|
|
||||||
|
|
||||||
|
|
||||||
{-| Convert a list of characters into a String. Can be useful if you
|
|
||||||
want to create a string primarily by consing, perhaps for decoding
|
|
||||||
something.
|
|
||||||
|
|
||||||
fromList ['a','b','c'] == "abc"
|
|
||||||
-}
|
|
||||||
fromList : List Char -> String
|
|
||||||
fromList =
|
|
||||||
Native.String.fromList
|
|
||||||
|
|
||||||
@@ -1,277 +0,0 @@
|
|||||||
effect module Task where { command = MyCmd } exposing
|
|
||||||
( Task
|
|
||||||
, succeed, fail
|
|
||||||
, map, map2, map3, map4, map5
|
|
||||||
, sequence
|
|
||||||
, andThen
|
|
||||||
, onError, mapError
|
|
||||||
, perform, attempt
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Tasks make it easy to describe asynchronous operations that may fail, like
|
|
||||||
HTTP requests or writing to a database. For more information, see the [Elm
|
|
||||||
documentation on Tasks](http://guide.elm-lang.org/error_handling/task.html).
|
|
||||||
|
|
||||||
# Basics
|
|
||||||
@docs Task, succeed, fail
|
|
||||||
|
|
||||||
# Mapping
|
|
||||||
@docs map, map2, map3, map4, map5
|
|
||||||
|
|
||||||
# Chaining
|
|
||||||
@docs andThen, sequence
|
|
||||||
|
|
||||||
# Errors
|
|
||||||
@docs onError, mapError
|
|
||||||
|
|
||||||
# Commands
|
|
||||||
@docs perform, attempt
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Basics exposing (Never, (|>), (<<))
|
|
||||||
import List exposing ((::))
|
|
||||||
import Maybe exposing (Maybe(Just,Nothing))
|
|
||||||
import Native.Scheduler
|
|
||||||
import Platform
|
|
||||||
import Platform.Cmd exposing (Cmd)
|
|
||||||
import Result exposing (Result(Ok,Err))
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{-| Represents asynchronous effects that may fail. It is useful for stuff like
|
|
||||||
HTTP.
|
|
||||||
|
|
||||||
For example, maybe we have a task with the type (`Task String User`). This means
|
|
||||||
that when we perform the task, it will either fail with a `String` message or
|
|
||||||
succeed with a `User`. So this could represent a task that is asking a server
|
|
||||||
for a certain user.
|
|
||||||
-}
|
|
||||||
type alias Task err ok =
|
|
||||||
Platform.Task err ok
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- BASICS
|
|
||||||
|
|
||||||
|
|
||||||
{-| A task that succeeds immediately when run.
|
|
||||||
|
|
||||||
succeed 42 -- results in 42
|
|
||||||
-}
|
|
||||||
succeed : a -> Task x a
|
|
||||||
succeed =
|
|
||||||
Native.Scheduler.succeed
|
|
||||||
|
|
||||||
|
|
||||||
{-| A task that fails immediately when run.
|
|
||||||
|
|
||||||
fail "file not found" : Task String a
|
|
||||||
-}
|
|
||||||
fail : x -> Task x a
|
|
||||||
fail =
|
|
||||||
Native.Scheduler.fail
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- MAPPING
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform a task.
|
|
||||||
|
|
||||||
map sqrt (succeed 9) -- succeed 3
|
|
||||||
-}
|
|
||||||
map : (a -> b) -> Task x a -> Task x b
|
|
||||||
map func taskA =
|
|
||||||
taskA
|
|
||||||
|> andThen (\a -> succeed (func a))
|
|
||||||
|
|
||||||
|
|
||||||
{-| Put the results of two tasks together. If either task fails, the whole
|
|
||||||
thing fails. It also runs in order so the first task will be completely
|
|
||||||
finished before the second task starts.
|
|
||||||
|
|
||||||
map2 (+) (succeed 9) (succeed 3) -- succeed 12
|
|
||||||
-}
|
|
||||||
map2 : (a -> b -> result) -> Task x a -> Task x b -> Task x result
|
|
||||||
map2 func taskA taskB =
|
|
||||||
taskA
|
|
||||||
|> andThen (\a -> taskB
|
|
||||||
|> andThen (\b -> succeed (func a b)))
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map3 : (a -> b -> c -> result) -> Task x a -> Task x b -> Task x c -> Task x result
|
|
||||||
map3 func taskA taskB taskC =
|
|
||||||
taskA
|
|
||||||
|> andThen (\a -> taskB
|
|
||||||
|> andThen (\b -> taskC
|
|
||||||
|> andThen (\c -> succeed (func a b c))))
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
map4 : (a -> b -> c -> d -> result) -> Task x a -> Task x b -> Task x c -> Task x d -> Task x result
|
|
||||||
map4 func taskA taskB taskC taskD =
|
|
||||||
taskA
|
|
||||||
|> andThen (\a -> taskB
|
|
||||||
|> andThen (\b -> taskC
|
|
||||||
|> andThen (\c -> taskD
|
|
||||||
|> andThen (\d -> succeed (func a b c d)))))
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
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
|
|
||||||
map5 func taskA taskB taskC taskD taskE =
|
|
||||||
taskA
|
|
||||||
|> andThen (\a -> taskB
|
|
||||||
|> andThen (\b -> taskC
|
|
||||||
|> andThen (\c -> taskD
|
|
||||||
|> andThen (\d -> taskE
|
|
||||||
|> andThen (\e -> succeed (func a b c d e))))))
|
|
||||||
|
|
||||||
|
|
||||||
{-| Start with a list of tasks, and turn them into a single task that returns a
|
|
||||||
list. The tasks will be run in order one-by-one and if any task fails the whole
|
|
||||||
sequence fails.
|
|
||||||
|
|
||||||
sequence [ succeed 1, succeed 2 ] -- succeed [ 1, 2 ]
|
|
||||||
|
|
||||||
This can be useful if you need to make a bunch of HTTP requests one-by-one.
|
|
||||||
-}
|
|
||||||
sequence : List (Task x a) -> Task x (List a)
|
|
||||||
sequence tasks =
|
|
||||||
case tasks of
|
|
||||||
[] ->
|
|
||||||
succeed []
|
|
||||||
|
|
||||||
task :: remainingTasks ->
|
|
||||||
map2 (::) task (sequence remainingTasks)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- CHAINING
|
|
||||||
|
|
||||||
|
|
||||||
{-| Chain together a task and a callback. The first task will run, and if it is
|
|
||||||
successful, you give the result to the callback resulting in another task. This
|
|
||||||
task then gets run.
|
|
||||||
|
|
||||||
succeed 2
|
|
||||||
|> andThen (\n -> succeed (n + 2))
|
|
||||||
-- succeed 4
|
|
||||||
|
|
||||||
This is useful for chaining tasks together. Maybe you need to get a user from
|
|
||||||
your servers *and then* lookup their picture once you know their name.
|
|
||||||
-}
|
|
||||||
andThen : (a -> Task x b) -> Task x a -> Task x b
|
|
||||||
andThen =
|
|
||||||
Native.Scheduler.andThen
|
|
||||||
|
|
||||||
|
|
||||||
-- ERRORS
|
|
||||||
|
|
||||||
{-| Recover from a failure in a task. If the given task fails, we use the
|
|
||||||
callback to recover.
|
|
||||||
|
|
||||||
fail "file not found"
|
|
||||||
|> onError (\msg -> succeed 42)
|
|
||||||
-- succeed 42
|
|
||||||
|
|
||||||
succeed 9
|
|
||||||
|> onError (\msg -> succeed 42)
|
|
||||||
-- succeed 9
|
|
||||||
-}
|
|
||||||
onError : (x -> Task y a) -> Task x a -> Task y a
|
|
||||||
onError =
|
|
||||||
Native.Scheduler.onError
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform the error value. This can be useful if you need a bunch of error
|
|
||||||
types to match up.
|
|
||||||
|
|
||||||
type Error = Http Http.Error | WebGL WebGL.Error
|
|
||||||
|
|
||||||
getResources : Task Error Resource
|
|
||||||
getResources =
|
|
||||||
sequence [ mapError Http serverTask, mapError WebGL textureTask ]
|
|
||||||
-}
|
|
||||||
mapError : (x -> y) -> Task x a -> Task y a
|
|
||||||
mapError convert task =
|
|
||||||
task
|
|
||||||
|> onError (fail << convert)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- COMMANDS
|
|
||||||
|
|
||||||
|
|
||||||
type MyCmd msg =
|
|
||||||
Perform (Task Never msg)
|
|
||||||
|
|
||||||
|
|
||||||
{-| The only way to *do* things in Elm is to give commands to the Elm runtime.
|
|
||||||
So we describe some complex behavior with a `Task` and then command the runtime
|
|
||||||
to `perform` that task. For example, getting the current time looks like this:
|
|
||||||
|
|
||||||
import Task
|
|
||||||
import Time exposing (Time)
|
|
||||||
|
|
||||||
type Msg = Click | NewTime Time
|
|
||||||
|
|
||||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
|
||||||
update msg model =
|
|
||||||
case msg of
|
|
||||||
Click ->
|
|
||||||
( model, Task.perform NewTime Time.now )
|
|
||||||
|
|
||||||
NewTime time ->
|
|
||||||
...
|
|
||||||
-}
|
|
||||||
perform : (a -> msg) -> Task Never a -> Cmd msg
|
|
||||||
perform toMessage task =
|
|
||||||
command (Perform (map toMessage task))
|
|
||||||
|
|
||||||
|
|
||||||
{-| Command the Elm runtime to attempt a task that might fail!
|
|
||||||
-}
|
|
||||||
attempt : (Result x a -> msg) -> Task x a -> Cmd msg
|
|
||||||
attempt resultToMessage task =
|
|
||||||
command (Perform (
|
|
||||||
task
|
|
||||||
|> andThen (succeed << resultToMessage << Ok)
|
|
||||||
|> onError (succeed << resultToMessage << Err)
|
|
||||||
))
|
|
||||||
|
|
||||||
|
|
||||||
cmdMap : (a -> b) -> MyCmd a -> MyCmd b
|
|
||||||
cmdMap tagger (Perform task) =
|
|
||||||
Perform (map tagger task)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- MANAGER
|
|
||||||
|
|
||||||
|
|
||||||
init : Task Never ()
|
|
||||||
init =
|
|
||||||
succeed ()
|
|
||||||
|
|
||||||
|
|
||||||
onEffects : Platform.Router msg Never -> List (MyCmd msg) -> () -> Task Never ()
|
|
||||||
onEffects router commands state =
|
|
||||||
map
|
|
||||||
(\_ -> ())
|
|
||||||
(sequence (List.map (spawnCmd router) commands))
|
|
||||||
|
|
||||||
|
|
||||||
onSelfMsg : Platform.Router msg Never -> Never -> () -> Task Never ()
|
|
||||||
onSelfMsg _ _ _ =
|
|
||||||
succeed ()
|
|
||||||
|
|
||||||
|
|
||||||
spawnCmd : Platform.Router msg Never -> MyCmd msg -> Task x ()
|
|
||||||
spawnCmd router (Perform task) =
|
|
||||||
Native.Scheduler.spawn (
|
|
||||||
task
|
|
||||||
|> andThen (Platform.sendToApp router)
|
|
||||||
)
|
|
||||||
@@ -1,243 +0,0 @@
|
|||||||
effect module Time where { subscription = MySub } exposing
|
|
||||||
( Time
|
|
||||||
, now, every
|
|
||||||
, millisecond, second, minute, hour
|
|
||||||
, inMilliseconds, inSeconds, inMinutes, inHours
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Library for working with time.
|
|
||||||
|
|
||||||
# Time
|
|
||||||
@docs Time, now, every
|
|
||||||
|
|
||||||
# Units
|
|
||||||
@docs millisecond, second, minute, hour,
|
|
||||||
inMilliseconds, inSeconds, inMinutes, inHours
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Dict
|
|
||||||
import List exposing ((::))
|
|
||||||
import Maybe exposing (Maybe(..))
|
|
||||||
import Native.Scheduler
|
|
||||||
import Native.Time
|
|
||||||
import Platform
|
|
||||||
import Platform.Sub exposing (Sub)
|
|
||||||
import Task exposing (Task)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- TIMES
|
|
||||||
|
|
||||||
|
|
||||||
{-| Type alias to make it clearer when you are working with time values.
|
|
||||||
Using the `Time` helpers like `second` and `inSeconds` instead of raw numbers
|
|
||||||
is very highly recommended.
|
|
||||||
-}
|
|
||||||
type alias Time = Float
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the `Time` at the moment when this task is run.
|
|
||||||
-}
|
|
||||||
now : Task x Time
|
|
||||||
now =
|
|
||||||
Native.Time.now
|
|
||||||
|
|
||||||
|
|
||||||
{-| Subscribe to the current time. First you provide an interval describing how
|
|
||||||
frequently you want updates. Second, you give a tagger that turns a time into a
|
|
||||||
message for your `update` function. So if you want to hear about the current
|
|
||||||
time every second, you would say something like this:
|
|
||||||
|
|
||||||
type Msg = Tick Time | ...
|
|
||||||
|
|
||||||
subscriptions model =
|
|
||||||
every second Tick
|
|
||||||
|
|
||||||
Check out the [Elm Architecture Tutorial][arch] for more info on how
|
|
||||||
subscriptions work.
|
|
||||||
|
|
||||||
[arch]: https://github.com/evancz/elm-architecture-tutorial/
|
|
||||||
|
|
||||||
**Note:** this function is not for animation! You need to use something based
|
|
||||||
on `requestAnimationFrame` to get smooth animations. This is based on
|
|
||||||
`setInterval` which is better for recurring tasks like “check on something
|
|
||||||
every 30 seconds”.
|
|
||||||
-}
|
|
||||||
every : Time -> (Time -> msg) -> Sub msg
|
|
||||||
every interval tagger =
|
|
||||||
subscription (Every interval tagger)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- UNITS
|
|
||||||
|
|
||||||
|
|
||||||
{-| Units of time, making it easier to specify things like a half-second
|
|
||||||
`(500 * millisecond)` without remembering Elm’s underlying units of time.
|
|
||||||
-}
|
|
||||||
millisecond : Time
|
|
||||||
millisecond =
|
|
||||||
1
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
second : Time
|
|
||||||
second =
|
|
||||||
1000 * millisecond
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
minute : Time
|
|
||||||
minute =
|
|
||||||
60 * second
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
hour : Time
|
|
||||||
hour =
|
|
||||||
60 * minute
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
inMilliseconds : Time -> Float
|
|
||||||
inMilliseconds t =
|
|
||||||
t
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
inSeconds : Time -> Float
|
|
||||||
inSeconds t =
|
|
||||||
t / second
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
inMinutes : Time -> Float
|
|
||||||
inMinutes t =
|
|
||||||
t / minute
|
|
||||||
|
|
||||||
|
|
||||||
{-|-}
|
|
||||||
inHours : Time -> Float
|
|
||||||
inHours t =
|
|
||||||
t / hour
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- SUBSCRIPTIONS
|
|
||||||
|
|
||||||
|
|
||||||
type MySub msg =
|
|
||||||
Every Time (Time -> msg)
|
|
||||||
|
|
||||||
|
|
||||||
subMap : (a -> b) -> MySub a -> MySub b
|
|
||||||
subMap f (Every interval tagger) =
|
|
||||||
Every interval (f << tagger)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- EFFECT MANAGER
|
|
||||||
|
|
||||||
|
|
||||||
type alias State msg =
|
|
||||||
{ taggers : Taggers msg
|
|
||||||
, processes : Processes
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
type alias Processes =
|
|
||||||
Dict.Dict Time Platform.ProcessId
|
|
||||||
|
|
||||||
|
|
||||||
type alias Taggers msg =
|
|
||||||
Dict.Dict Time (List (Time -> msg))
|
|
||||||
|
|
||||||
|
|
||||||
init : Task Never (State msg)
|
|
||||||
init =
|
|
||||||
Task.succeed (State Dict.empty Dict.empty)
|
|
||||||
|
|
||||||
|
|
||||||
onEffects : Platform.Router msg Time -> List (MySub msg) -> State msg -> Task Never (State msg)
|
|
||||||
onEffects router subs {processes} =
|
|
||||||
let
|
|
||||||
newTaggers =
|
|
||||||
List.foldl addMySub Dict.empty subs
|
|
||||||
|
|
||||||
leftStep interval taggers (spawnList, existingDict, killTask) =
|
|
||||||
(interval :: spawnList, existingDict, killTask)
|
|
||||||
|
|
||||||
bothStep interval taggers id (spawnList, existingDict, killTask) =
|
|
||||||
(spawnList, Dict.insert interval id existingDict, killTask)
|
|
||||||
|
|
||||||
rightStep _ id (spawnList, existingDict, killTask) =
|
|
||||||
( spawnList
|
|
||||||
, existingDict
|
|
||||||
, Native.Scheduler.kill id
|
|
||||||
|> Task.andThen (\_ -> killTask)
|
|
||||||
)
|
|
||||||
|
|
||||||
(spawnList, existingDict, killTask) =
|
|
||||||
Dict.merge
|
|
||||||
leftStep
|
|
||||||
bothStep
|
|
||||||
rightStep
|
|
||||||
newTaggers
|
|
||||||
processes
|
|
||||||
([], Dict.empty, Task.succeed ())
|
|
||||||
in
|
|
||||||
killTask
|
|
||||||
|> Task.andThen (\_ -> spawnHelp router spawnList existingDict)
|
|
||||||
|> Task.andThen (\newProcesses -> Task.succeed (State newTaggers newProcesses))
|
|
||||||
|
|
||||||
|
|
||||||
addMySub : MySub msg -> Taggers msg -> Taggers msg
|
|
||||||
addMySub (Every interval tagger) state =
|
|
||||||
case Dict.get interval state of
|
|
||||||
Nothing ->
|
|
||||||
Dict.insert interval [tagger] state
|
|
||||||
|
|
||||||
Just taggers ->
|
|
||||||
Dict.insert interval (tagger :: taggers) state
|
|
||||||
|
|
||||||
|
|
||||||
spawnHelp : Platform.Router msg Time -> List Time -> Processes -> Task.Task x Processes
|
|
||||||
spawnHelp router intervals processes =
|
|
||||||
case intervals of
|
|
||||||
[] ->
|
|
||||||
Task.succeed processes
|
|
||||||
|
|
||||||
interval :: rest ->
|
|
||||||
let
|
|
||||||
spawnTimer =
|
|
||||||
Native.Scheduler.spawn (setInterval interval (Platform.sendToSelf router interval))
|
|
||||||
|
|
||||||
spawnRest id =
|
|
||||||
spawnHelp router rest (Dict.insert interval id processes)
|
|
||||||
in
|
|
||||||
spawnTimer
|
|
||||||
|> Task.andThen spawnRest
|
|
||||||
|
|
||||||
|
|
||||||
onSelfMsg : Platform.Router msg Time -> Time -> State msg -> Task Never (State msg)
|
|
||||||
onSelfMsg router interval state =
|
|
||||||
case Dict.get interval state.taggers of
|
|
||||||
Nothing ->
|
|
||||||
Task.succeed state
|
|
||||||
|
|
||||||
Just taggers ->
|
|
||||||
let
|
|
||||||
tellTaggers time =
|
|
||||||
Task.sequence (List.map (\tagger -> Platform.sendToApp router (tagger time)) taggers)
|
|
||||||
in
|
|
||||||
now
|
|
||||||
|> Task.andThen tellTaggers
|
|
||||||
|> Task.andThen (\_ -> Task.succeed state)
|
|
||||||
|
|
||||||
|
|
||||||
setInterval : Time -> Task Never () -> Task x Never
|
|
||||||
setInterval =
|
|
||||||
Native.Time.setInterval_
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
module Tuple exposing
|
|
||||||
( first, second
|
|
||||||
, mapFirst, mapSecond
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| Some helpers for working with 2-tuples.
|
|
||||||
|
|
||||||
**Note:** For larger chunks of data, it is best to switch to using records. So
|
|
||||||
instead of representing a 3D point as `(3,4,5)` and wondering why there are no
|
|
||||||
helper functions, represent it as `{ x = 3, y = 4, z = 5 }` and use all the
|
|
||||||
built-in syntax for records.
|
|
||||||
|
|
||||||
@docs first, second, mapFirst, mapSecond
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the first value from a tuple.
|
|
||||||
|
|
||||||
first (3, 4) == 3
|
|
||||||
first ("john", "doe") == "john"
|
|
||||||
-}
|
|
||||||
first : (a1, a2) -> a1
|
|
||||||
first (x,_) =
|
|
||||||
x
|
|
||||||
|
|
||||||
|
|
||||||
{-| Extract the second value from a tuple.
|
|
||||||
|
|
||||||
second (3, 4) == 4
|
|
||||||
second ("john", "doe") == "doe"
|
|
||||||
-}
|
|
||||||
second : (a1, a2) -> a2
|
|
||||||
second (_,y) =
|
|
||||||
y
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform the first value in a tuple.
|
|
||||||
|
|
||||||
import String
|
|
||||||
|
|
||||||
mapFirst String.reverse ("stressed", 16) == ("desserts", 16)
|
|
||||||
mapFirst String.length ("stressed", 16) == (8, 16)
|
|
||||||
-}
|
|
||||||
mapFirst : (a -> b) -> (a, a2) -> (b, a2)
|
|
||||||
mapFirst func (x,y) =
|
|
||||||
(func x, y)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Transform the second value in a tuple.
|
|
||||||
|
|
||||||
import String
|
|
||||||
|
|
||||||
mapSecond sqrt ("stressed", 16) == ("stressed", 4)
|
|
||||||
mapSecond (\x -> x + 1) ("stressed", 16) == ("stressed", 17)
|
|
||||||
-}
|
|
||||||
mapSecond : (a -> b) -> (a1, a) -> (a1, b)
|
|
||||||
mapSecond func (x,y) =
|
|
||||||
(x, func y)
|
|
||||||
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
port module Main exposing (..)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Task exposing (..)
|
|
||||||
import Test exposing (..)
|
|
||||||
import Platform.Cmd exposing (Cmd)
|
|
||||||
import Json.Decode exposing (Value)
|
|
||||||
import Test.Runner.Node exposing (run, TestProgram)
|
|
||||||
import Test.Array as Array
|
|
||||||
import Test.Basics as Basics
|
|
||||||
import Test.Bitwise as Bitwise
|
|
||||||
import Test.Char as Char
|
|
||||||
import Test.CodeGen as CodeGen
|
|
||||||
import Test.Dict as Dict
|
|
||||||
import Test.Maybe as Maybe
|
|
||||||
import Test.Equality as Equality
|
|
||||||
import Test.Json as Json
|
|
||||||
import Test.List as List
|
|
||||||
import Test.Result as Result
|
|
||||||
import Test.Set as Set
|
|
||||||
import Test.String as String
|
|
||||||
import Test.Regex as Regex
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
describe "Elm Standard Library Tests"
|
|
||||||
[ Array.tests
|
|
||||||
, Basics.tests
|
|
||||||
, Bitwise.tests
|
|
||||||
, Char.tests
|
|
||||||
, CodeGen.tests
|
|
||||||
, Dict.tests
|
|
||||||
, Equality.tests
|
|
||||||
, Json.tests
|
|
||||||
, List.tests
|
|
||||||
, Result.tests
|
|
||||||
, Set.tests
|
|
||||||
, String.tests
|
|
||||||
, Regex.tests
|
|
||||||
, Maybe.tests
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
main : TestProgram
|
|
||||||
main =
|
|
||||||
run emit tests
|
|
||||||
|
|
||||||
|
|
||||||
port emit : ( String, Value ) -> Cmd msg
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
module Test.Array exposing (tests)
|
|
||||||
|
|
||||||
import Array
|
|
||||||
import Basics exposing (..)
|
|
||||||
import List
|
|
||||||
import List exposing ((::))
|
|
||||||
import Maybe exposing (..)
|
|
||||||
import Native.Array
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
mergeSplit : Int -> Array.Array a -> Array.Array a
|
|
||||||
mergeSplit n arr =
|
|
||||||
let
|
|
||||||
left =
|
|
||||||
Array.slice 0 n arr
|
|
||||||
|
|
||||||
right =
|
|
||||||
Array.slice n (Array.length arr) arr
|
|
||||||
in
|
|
||||||
Array.append left right
|
|
||||||
|
|
||||||
|
|
||||||
holeArray : Array.Array Int
|
|
||||||
holeArray =
|
|
||||||
List.foldl mergeSplit (Array.fromList (List.range 0 100)) (List.range 0 100)
|
|
||||||
|
|
||||||
|
|
||||||
mapArray : Array.Array a -> Array.Array a
|
|
||||||
mapArray array =
|
|
||||||
Array.indexedMap
|
|
||||||
(\i el ->
|
|
||||||
case (Array.get i array) of
|
|
||||||
Just x ->
|
|
||||||
x
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
el
|
|
||||||
)
|
|
||||||
array
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
creationTests =
|
|
||||||
describe "Creation"
|
|
||||||
[ test "empty" <| \() -> Expect.equal Array.empty (Array.fromList [])
|
|
||||||
, test "initialize" <| \() -> Expect.equal (Array.initialize 4 identity) (Array.fromList [ 0, 1, 2, 3 ])
|
|
||||||
, test "initialize 2" <| \() -> Expect.equal (Array.initialize 4 (\n -> n * n)) (Array.fromList [ 0, 1, 4, 9 ])
|
|
||||||
, test "initialize 3" <| \() -> Expect.equal (Array.initialize 4 (always 0)) (Array.fromList [ 0, 0, 0, 0 ])
|
|
||||||
, test "initialize Empty" <| \() -> Expect.equal (Array.initialize 0 identity) Array.empty
|
|
||||||
, test "initialize 4" <| \() -> Expect.equal (Array.initialize 2 (always 0)) (Array.fromList [ 0, 0 ])
|
|
||||||
, test "initialize negative" <| \() -> Expect.equal (Array.initialize -1 identity) Array.empty
|
|
||||||
, test "repeat" <| \() -> Expect.equal (Array.repeat 5 40) (Array.fromList [ 40, 40, 40, 40, 40 ])
|
|
||||||
, test "repeat 2" <| \() -> Expect.equal (Array.repeat 5 0) (Array.fromList [ 0, 0, 0, 0, 0 ])
|
|
||||||
, test "repeat 3" <| \() -> Expect.equal (Array.repeat 3 "cat") (Array.fromList [ "cat", "cat", "cat" ])
|
|
||||||
, test "fromList" <| \() -> Expect.equal (Array.fromList []) Array.empty
|
|
||||||
]
|
|
||||||
|
|
||||||
basicsTests =
|
|
||||||
describe "Basics"
|
|
||||||
[ test "length" <| \() -> Expect.equal 3 (Array.length (Array.fromList [ 1, 2, 3 ]))
|
|
||||||
, test "length - Long" <| \() -> Expect.equal 10000 (Array.length (Array.repeat 10000 0))
|
|
||||||
, test "push" <| \() -> Expect.equal (Array.fromList [ 1, 2, 3 ]) (Array.push 3 (Array.fromList [ 1, 2 ]))
|
|
||||||
, test "append" <| \() -> Expect.equal [ 42, 42, 81, 81, 81 ] (Array.toList (Array.append (Array.repeat 2 42) (Array.repeat 3 81)))
|
|
||||||
, test "appendEmpty 1" <| \() -> Expect.equal (List.range 1 33) (Array.toList (Array.append Array.empty (Array.fromList <| List.range 1 33)))
|
|
||||||
, test "appendEmpty 2" <| \() -> Expect.equal (List.range 1 33) (Array.toList (Array.append (Array.fromList <| List.range 1 33) Array.empty))
|
|
||||||
, test "appendSmall 1" <| \() -> Expect.equal (List.range 1 33) (Array.toList (Array.append (Array.fromList <| List.range 1 30) (Array.fromList <| List.range 31 33)))
|
|
||||||
, test "appendSmall 2" <| \() -> Expect.equal (List.range 1 33) (Array.toList (Array.append (Array.fromList <| List.range 1 3) (Array.fromList <| List.range 4 33)))
|
|
||||||
, test "appendAndSlice" <| \() -> Expect.equal (List.range 0 100) (Array.toList holeArray)
|
|
||||||
]
|
|
||||||
|
|
||||||
getAndSetTests =
|
|
||||||
describe "Get and Set"
|
|
||||||
[ test "get" <| \() -> Expect.equal (Just 2) (Array.get 1 (Array.fromList [ 3, 2, 1 ]))
|
|
||||||
, test "get 2" <| \() -> Expect.equal Nothing (Array.get 5 (Array.fromList [ 3, 2, 1 ]))
|
|
||||||
, test "get 3" <| \() -> Expect.equal Nothing (Array.get -1 (Array.fromList [ 3, 2, 1 ]))
|
|
||||||
, test "set" <| \() -> Expect.equal (Array.fromList [ 1, 7, 3 ]) (Array.set 1 7 (Array.fromList [ 1, 2, 3 ]))
|
|
||||||
]
|
|
||||||
|
|
||||||
takingArraysApartTests =
|
|
||||||
describe "Taking Arrays Apart"
|
|
||||||
[ test "toList" <| \() -> Expect.equal [ 3, 5, 8 ] (Array.toList (Array.fromList [ 3, 5, 8 ]))
|
|
||||||
, test "toIndexedList" <| \() -> Expect.equal [ ( 0, "cat" ), ( 1, "dog" ) ] (Array.toIndexedList (Array.fromList [ "cat", "dog" ]))
|
|
||||||
, test "slice 1" <| \() -> Expect.equal (Array.fromList [ 0, 1, 2 ]) (Array.slice 0 3 (Array.fromList [ 0, 1, 2, 3, 4 ]))
|
|
||||||
, test "slice 2" <| \() -> Expect.equal (Array.fromList [ 1, 2, 3 ]) (Array.slice 1 4 (Array.fromList [ 0, 1, 2, 3, 4 ]))
|
|
||||||
, test "slice 3" <| \() -> Expect.equal (Array.fromList [ 1, 2, 3 ]) (Array.slice 1 -1 (Array.fromList [ 0, 1, 2, 3, 4 ]))
|
|
||||||
, test "slice 4" <| \() -> Expect.equal (Array.fromList [ 2 ]) (Array.slice -3 -2 (Array.fromList [ 0, 1, 2, 3, 4 ]))
|
|
||||||
, test "slice 5" <| \() -> Expect.equal 63 (Array.length <| Array.slice 65 (65 + 63) <| Array.fromList (List.range 1 200))
|
|
||||||
]
|
|
||||||
|
|
||||||
mappingAndFoldingTests =
|
|
||||||
describe "Mapping and Folding"
|
|
||||||
[ test "map" <| \() -> Expect.equal (Array.fromList [ 1, 2, 3 ]) (Array.map sqrt (Array.fromList [ 1, 4, 9 ]))
|
|
||||||
, test "indexedMap 1" <| \() -> Expect.equal (Array.fromList [ 0, 5, 10 ]) (Array.indexedMap (*) (Array.fromList [ 5, 5, 5 ]))
|
|
||||||
, test "indexedMap 2" <| \() -> Expect.equal (List.range 0 99) (Array.toList (Array.indexedMap always (Array.repeat 100 0)))
|
|
||||||
, test "large indexed map" <| \() -> Expect.equal (List.range 0 <| 32768 - 1) (Array.toList <| mapArray <| Array.initialize 32768 identity)
|
|
||||||
, test "foldl 1" <| \() -> Expect.equal [ 3, 2, 1 ] (Array.foldl (::) [] (Array.fromList [ 1, 2, 3 ]))
|
|
||||||
, test "foldl 2" <| \() -> Expect.equal 33 (Array.foldl (+) 0 (Array.repeat 33 1))
|
|
||||||
, test "foldr 1" <| \() -> Expect.equal 15 (Array.foldr (+) 0 (Array.repeat 3 5))
|
|
||||||
, test "foldr 2" <| \() -> Expect.equal [ 1, 2, 3 ] (Array.foldr (::) [] (Array.fromList [ 1, 2, 3 ]))
|
|
||||||
, test "foldr 3" <| \() -> Expect.equal 53 (Array.foldr (-) 54 (Array.fromList [ 10, 11 ]))
|
|
||||||
, test "filter" <| \() -> Expect.equal (Array.fromList [ 2, 4, 6 ]) (Array.filter (\x -> x % 2 == 0) (Array.fromList <| List.range 1 6))
|
|
||||||
]
|
|
||||||
|
|
||||||
nativeTests =
|
|
||||||
describe "Conversion to JS Arrays"
|
|
||||||
[ test "jsArrays" <| \() -> Expect.equal (Array.fromList <| List.range 1 1100) (Native.Array.fromJSArray (Native.Array.toJSArray (Array.fromList <| List.range 1 1100)))
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "Array"
|
|
||||||
[ creationTests
|
|
||||||
, basicsTests
|
|
||||||
, getAndSetTests
|
|
||||||
, takingArraysApartTests
|
|
||||||
, mappingAndFoldingTests
|
|
||||||
, nativeTests
|
|
||||||
]
|
|
||||||
@@ -1,220 +0,0 @@
|
|||||||
module Test.Basics exposing (tests)
|
|
||||||
|
|
||||||
import Array
|
|
||||||
import Tuple exposing (first, second)
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Date
|
|
||||||
import Set
|
|
||||||
import Dict
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
import List
|
|
||||||
import String
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
comparison =
|
|
||||||
describe "Comparison"
|
|
||||||
[ test "max" <| \() -> Expect.equal 42 (max 32 42)
|
|
||||||
, test "min" <| \() -> Expect.equal 42 (min 91 42)
|
|
||||||
, test "clamp low" <| \() -> Expect.equal 10 (clamp 10 20 5)
|
|
||||||
, test "clamp mid" <| \() -> Expect.equal 15 (clamp 10 20 15)
|
|
||||||
, test "clamp high" <| \() -> Expect.equal 20 (clamp 10 20 25)
|
|
||||||
, test "5 < 6" <| \() -> Expect.equal True (5 < 6)
|
|
||||||
, test "6 < 5" <| \() -> Expect.equal False (6 < 5)
|
|
||||||
, test "6 < 6" <| \() -> Expect.equal False (6 < 6)
|
|
||||||
, test "5 > 6" <| \() -> Expect.equal False (5 > 6)
|
|
||||||
, test "6 > 5" <| \() -> Expect.equal True (6 > 5)
|
|
||||||
, test "6 > 6" <| \() -> Expect.equal False (6 > 6)
|
|
||||||
, test "5 <= 6" <| \() -> Expect.equal True (5 <= 6)
|
|
||||||
, test "6 <= 5" <| \() -> Expect.equal False (6 <= 5)
|
|
||||||
, test "6 <= 6" <| \() -> Expect.equal True (6 <= 6)
|
|
||||||
, test "compare \"A\" \"B\"" <| \() -> Expect.equal LT (compare "A" "B")
|
|
||||||
, test "compare 'f' 'f'" <| \() -> Expect.equal EQ (compare 'f' 'f')
|
|
||||||
, 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 ))
|
|
||||||
, test "compare ['a'] ['b']" <| \() -> Expect.equal LT (compare [ 'a' ] [ 'b' ])
|
|
||||||
, test "array equality" <| \() -> Expect.equal (Array.fromList [ 1, 1, 1, 1 ]) (Array.repeat 4 1)
|
|
||||||
, test "set equality" <| \() -> Expect.equal (Set.fromList [ 1, 2 ]) (Set.fromList [ 2, 1 ])
|
|
||||||
, test "dict equality" <| \() -> Expect.equal (Dict.fromList [ ( 1, 1 ), ( 2, 2 ) ]) (Dict.fromList [ ( 2, 2 ), ( 1, 1 ) ])
|
|
||||||
, test "char equality" <| \() -> Expect.notEqual '0' '饑'
|
|
||||||
, test "date equality" <| \() -> Expect.equal (Date.fromString "2/7/1992") (Date.fromString "2/7/1992")
|
|
||||||
, test "date equality" <| \() -> Expect.notEqual (Date.fromString "11/16/1995") (Date.fromString "2/7/1992")
|
|
||||||
]
|
|
||||||
|
|
||||||
toStringTests =
|
|
||||||
describe "toString Tests"
|
|
||||||
[ test "toString Int" <| \() -> Expect.equal "42" (toString 42)
|
|
||||||
, test "toString Float" <| \() -> Expect.equal "42.52" (toString 42.52)
|
|
||||||
, test "toString Char" <| \() -> Expect.equal "'c'" (toString 'c')
|
|
||||||
, test "toString Char single quote" <| \() -> Expect.equal "'\\''" (toString '\'')
|
|
||||||
, test "toString Char double quote" <| \() -> Expect.equal "'\"'" (toString '"')
|
|
||||||
, test "toString String single quote" <| \() -> Expect.equal "\"not 'escaped'\"" (toString "not 'escaped'")
|
|
||||||
, test "toString String double quote" <| \() -> Expect.equal "\"are \\\"escaped\\\"\"" (toString "are \"escaped\"")
|
|
||||||
, test "toString record" <| \() -> Expect.equal "{ field = [0] }" (toString { field = [ 0 ] })
|
|
||||||
-- TODO
|
|
||||||
--, test "toString record, special case" <| \() -> Expect.equal "{ ctor = [0] }" (toString { ctor = [ 0 ] })
|
|
||||||
]
|
|
||||||
|
|
||||||
trigTests =
|
|
||||||
describe "Trigonometry Tests"
|
|
||||||
[ test "radians 0" <| \() -> Expect.equal 0 (radians 0)
|
|
||||||
, test "radians positive" <| \() -> Expect.equal 5 (radians 5)
|
|
||||||
, test "radians negative" <| \() -> Expect.equal -5 (radians -5)
|
|
||||||
, test "degrees 0" <| \() -> Expect.equal 0 (degrees 0)
|
|
||||||
, test "degrees 90" <| \() -> Expect.lessThan 0.01 (abs (1.57 - degrees 90))
|
|
||||||
-- This should test to enough precision to know if anything's breaking
|
|
||||||
, test "degrees -145" <| \() -> Expect.lessThan 0.01 (abs (-2.53 - degrees -145))
|
|
||||||
-- This should test to enough precision to know if anything's breaking
|
|
||||||
, test "turns 0" <| \() -> Expect.equal 0 (turns 0)
|
|
||||||
, test "turns 8" <| \() -> Expect.lessThan 0.01 (abs (50.26 - turns 8))
|
|
||||||
-- This should test to enough precision to know if anything's breaking
|
|
||||||
, test "turns -133" <| \() -> Expect.lessThan 0.01 (abs (-835.66 - turns -133))
|
|
||||||
-- This should test to enough precision to know if anything's breaking
|
|
||||||
, test "fromPolar (0, 0)" <| \() -> Expect.equal ( 0, 0 ) (fromPolar ( 0, 0 ))
|
|
||||||
, test "fromPolar (1, 0)" <| \() -> Expect.equal ( 1, 0 ) (fromPolar ( 1, 0 ))
|
|
||||||
, test "fromPolar (0, 1)" <| \() -> Expect.equal ( 0, 0 ) (fromPolar ( 0, 1 ))
|
|
||||||
, test "fromPolar (1, 1)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal True
|
|
||||||
(let
|
|
||||||
( x, y ) =
|
|
||||||
fromPolar ( 1, 1 )
|
|
||||||
in
|
|
||||||
0.54 - x < 0.01 && 0.84 - y < 0.01
|
|
||||||
)
|
|
||||||
, test "toPolar (0, 0)" <| \() -> Expect.equal ( 0, 0 ) (toPolar ( 0, 0 ))
|
|
||||||
, test "toPolar (1, 0)" <| \() -> Expect.equal ( 1, 0 ) (toPolar ( 1, 0 ))
|
|
||||||
, test "toPolar (0, 1)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal True
|
|
||||||
(let
|
|
||||||
( r, theta ) =
|
|
||||||
toPolar ( 0, 1 )
|
|
||||||
in
|
|
||||||
r == 1 && abs (1.57 - theta) < 0.01
|
|
||||||
)
|
|
||||||
, test "toPolar (1, 1)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal True
|
|
||||||
(let
|
|
||||||
( r, theta ) =
|
|
||||||
toPolar ( 1, 1 )
|
|
||||||
in
|
|
||||||
abs (1.41 - r) < 0.01 && abs (0.78 - theta) < 0.01
|
|
||||||
)
|
|
||||||
, test "cos" <| \() -> Expect.equal 1 (cos 0)
|
|
||||||
, test "sin" <| \() -> Expect.equal 0 (sin 0)
|
|
||||||
, test "tan" <| \() -> Expect.lessThan 0.01 (abs (12.67 - tan 17.2))
|
|
||||||
, test "acos" <| \() -> Expect.lessThan 0.01 (abs (3.14 - acos -1))
|
|
||||||
, test "asin" <| \() -> Expect.lessThan 0.01 (abs (0.3 - asin 0.3))
|
|
||||||
, test "atan" <| \() -> Expect.lessThan 0.01 (abs (1.57 - atan 4567.8))
|
|
||||||
, test "atan2" <| \() -> Expect.lessThan 0.01 (abs (1.55 - atan2 36 0.65))
|
|
||||||
, test "pi" <| \() -> Expect.lessThan 0.01 (abs (3.14 - pi))
|
|
||||||
]
|
|
||||||
|
|
||||||
basicMathTests =
|
|
||||||
describe "Basic Math Tests"
|
|
||||||
[ test "add float" <| \() -> Expect.equal 159 (155.6 + 3.4)
|
|
||||||
, test "add int" <| \() -> Expect.equal 17 ((round 10) + (round 7))
|
|
||||||
, test "subtract float" <| \() -> Expect.equal -6.3 (1 - 7.3)
|
|
||||||
, test "subtract int" <| \() -> Expect.equal 1130 ((round 9432) - (round 8302))
|
|
||||||
, test "multiply float" <| \() -> Expect.equal 432 (96 * 4.5)
|
|
||||||
, test "multiply int" <| \() -> Expect.equal 90 ((round 10) * (round 9))
|
|
||||||
, test "divide float" <| \() -> Expect.equal 13.175 (527 / 40)
|
|
||||||
, test "divide int" <| \() -> Expect.equal 23 (70 // 3)
|
|
||||||
, test "2 |> rem 7" <| \() -> Expect.equal 1 (2 |> rem 7)
|
|
||||||
, test "4 |> rem -1" <| \() -> Expect.equal -1 (4 |> rem -1)
|
|
||||||
, test "7 % 2" <| \() -> Expect.equal 1 (7 % 2)
|
|
||||||
, test "-1 % 4" <| \() -> Expect.equal 3 (-1 % 4)
|
|
||||||
, test "3^2" <| \() -> Expect.equal 9 (3 ^ 2)
|
|
||||||
, test "sqrt" <| \() -> Expect.equal 9 (sqrt 81)
|
|
||||||
, test "negate 42" <| \() -> Expect.equal -42 (negate 42)
|
|
||||||
, test "negate -42" <| \() -> Expect.equal 42 (negate -42)
|
|
||||||
, test "negate 0" <| \() -> Expect.equal 0 (negate 0)
|
|
||||||
, test "abs -25" <| \() -> Expect.equal 25 (abs -25)
|
|
||||||
, test "abs 76" <| \() -> Expect.equal 76 (abs 76)
|
|
||||||
, test "logBase 10 100" <| \() -> Expect.equal 2 (logBase 10 100)
|
|
||||||
, test "logBase 2 256" <| \() -> Expect.equal 8 (logBase 2 256)
|
|
||||||
, test "e" <| \() -> Expect.lessThan 0.01 (abs (2.72 - e))
|
|
||||||
]
|
|
||||||
|
|
||||||
booleanTests =
|
|
||||||
describe "Boolean Tests"
|
|
||||||
[ test "False && False" <| \() -> Expect.equal False (False && False)
|
|
||||||
, test "False && True" <| \() -> Expect.equal False (False && True)
|
|
||||||
, test "True && False" <| \() -> Expect.equal False (True && False)
|
|
||||||
, test "True && True" <| \() -> Expect.equal True (True && True)
|
|
||||||
, test "False || False" <| \() -> Expect.equal False (False || False)
|
|
||||||
, test "False || True" <| \() -> Expect.equal True (False || True)
|
|
||||||
, test "True || False" <| \() -> Expect.equal True (True || False)
|
|
||||||
, test "True || True" <| \() -> Expect.equal True (True || True)
|
|
||||||
, test "xor False False" <| \() -> Expect.equal False (xor False False)
|
|
||||||
, test "xor False True" <| \() -> Expect.equal True (xor False True)
|
|
||||||
, test "xor True False" <| \() -> Expect.equal True (xor True False)
|
|
||||||
, test "xor True True" <| \() -> Expect.equal False (xor True True)
|
|
||||||
, test "not True" <| \() -> Expect.equal False (not True)
|
|
||||||
, test "not False" <| \() -> Expect.equal True (not False)
|
|
||||||
]
|
|
||||||
|
|
||||||
conversionTests =
|
|
||||||
describe "Conversion Tests"
|
|
||||||
[ test "round 0.6" <| \() -> Expect.equal 1 (round 0.6)
|
|
||||||
, test "round 0.4" <| \() -> Expect.equal 0 (round 0.4)
|
|
||||||
, test "round 0.5" <| \() -> Expect.equal 1 (round 0.5)
|
|
||||||
, test "truncate -2367.9267" <| \() -> Expect.equal -2367 (truncate -2367.9267)
|
|
||||||
, test "floor -2367.9267" <| \() -> Expect.equal -2368 (floor -2367.9267)
|
|
||||||
, test "ceiling 37.2" <| \() -> Expect.equal 38 (ceiling 37.2)
|
|
||||||
, test "toFloat 25" <| \() -> Expect.equal 25 (toFloat 25)
|
|
||||||
]
|
|
||||||
|
|
||||||
miscTests =
|
|
||||||
describe "Miscellaneous Tests"
|
|
||||||
[ test "isNaN (0/0)" <| \() -> Expect.equal True (isNaN (0 / 0))
|
|
||||||
, test "isNaN (sqrt -1)" <| \() -> Expect.equal True (isNaN (sqrt -1))
|
|
||||||
, test "isNaN (1/0)" <| \() -> Expect.equal False (isNaN (1 / 0))
|
|
||||||
, test "isNaN 1" <| \() -> Expect.equal False (isNaN 1)
|
|
||||||
, test "isInfinite (0/0)" <| \() -> Expect.equal False (isInfinite (0 / 0))
|
|
||||||
, test "isInfinite (sqrt -1)" <| \() -> Expect.equal False (isInfinite (sqrt -1))
|
|
||||||
, test "isInfinite (1/0)" <| \() -> Expect.equal True (isInfinite (1 / 0))
|
|
||||||
, test "isInfinite 1" <| \() -> Expect.equal False (isInfinite 1)
|
|
||||||
, test "\"hello\" ++ \"world\"" <| \() -> Expect.equal "helloworld" ("hello" ++ "world")
|
|
||||||
, test "[1, 1, 2] ++ [3, 5, 8]" <| \() -> Expect.equal [ 1, 1, 2, 3, 5, 8 ] ([ 1, 1, 2 ] ++ [ 3, 5, 8 ])
|
|
||||||
, test "first (1, 2)" <| \() -> Expect.equal 1 (first ( 1, 2 ))
|
|
||||||
, test "second (1, 2)" <| \() -> Expect.equal 2 (second ( 1, 2 ))
|
|
||||||
]
|
|
||||||
|
|
||||||
higherOrderTests =
|
|
||||||
describe "Higher Order Helpers"
|
|
||||||
[ test "identity 'c'" <| \() -> Expect.equal 'c' (identity 'c')
|
|
||||||
, test "always 42 ()" <| \() -> Expect.equal 42 (always 42 ())
|
|
||||||
, test "<|" <| \() -> Expect.equal 9 (identity <| 3 + 6)
|
|
||||||
, test "|>" <| \() -> Expect.equal 9 (3 + 6 |> identity)
|
|
||||||
, test "<<" <| \() -> Expect.equal True (not << xor True <| True)
|
|
||||||
, test "<<" <| \() -> Expect.equal True (not << xor True <| True)
|
|
||||||
, describe ">>"
|
|
||||||
[ test "with xor" <|
|
|
||||||
\() ->
|
|
||||||
(True |> xor True >> not)
|
|
||||||
|> Expect.equal True
|
|
||||||
, test "with a record accessor" <|
|
|
||||||
\() ->
|
|
||||||
[ { foo = "NaS", bar = "baz" } ]
|
|
||||||
|> List.map (.foo >> String.reverse)
|
|
||||||
|> Expect.equal [ "SaN" ]
|
|
||||||
]
|
|
||||||
, test "flip" <| \() -> Expect.equal 10 ((flip (//)) 2 20)
|
|
||||||
, test "curry" <| \() -> Expect.equal 1 ((curry (\( a, b ) -> a + b)) -5 6)
|
|
||||||
, test "uncurry" <| \() -> Expect.equal 1 ((uncurry (+)) ( -5, 6 ))
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "Basics"
|
|
||||||
[ comparison
|
|
||||||
, toStringTests
|
|
||||||
, trigTests
|
|
||||||
, basicMathTests
|
|
||||||
, booleanTests
|
|
||||||
, miscTests
|
|
||||||
, higherOrderTests
|
|
||||||
]
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
module Test.Bitwise exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Bitwise
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
describe "Bitwise"
|
|
||||||
[ describe "and"
|
|
||||||
[ test "and with 32 bit integers" <| \() -> Expect.equal 1 (Bitwise.and 5 3)
|
|
||||||
, test "and with 0 as first argument" <| \() -> Expect.equal 0 (Bitwise.and 0 1450)
|
|
||||||
, test "and with 0 as second argument" <| \() -> Expect.equal 0 (Bitwise.and 274 0)
|
|
||||||
, test "and with -1 as first argument" <| \() -> Expect.equal 2671 (Bitwise.and -1 2671)
|
|
||||||
, test "and with -1 as second argument" <| \() -> Expect.equal 96 (Bitwise.and 96 -1)
|
|
||||||
]
|
|
||||||
, describe "or"
|
|
||||||
[ test "or with 32 bit integers" <| \() -> Expect.equal 15 (Bitwise.or 9 14)
|
|
||||||
, test "or with 0 as first argument" <| \() -> Expect.equal 843 (Bitwise.or 0 843)
|
|
||||||
, test "or with 0 as second argument" <| \() -> Expect.equal 19 (Bitwise.or 19 0)
|
|
||||||
, test "or with -1 as first argument" <| \() -> Expect.equal -1 (Bitwise.or -1 2360)
|
|
||||||
, test "or with -1 as second argument" <| \() -> Expect.equal -1 (Bitwise.or 3 -1)
|
|
||||||
]
|
|
||||||
, describe "xor"
|
|
||||||
[ test "xor with 32 bit integers" <| \() -> Expect.equal 604 (Bitwise.xor 580 24)
|
|
||||||
, test "xor with 0 as first argument" <| \() -> Expect.equal 56 (Bitwise.xor 0 56)
|
|
||||||
, test "xor with 0 as second argument" <| \() -> Expect.equal -268 (Bitwise.xor -268 0)
|
|
||||||
, test "xor with -1 as first argument" <| \() -> Expect.equal -25 (Bitwise.xor -1 24)
|
|
||||||
, test "xor with -1 as second argument" <| \() -> Expect.equal 25601 (Bitwise.xor -25602 -1)
|
|
||||||
]
|
|
||||||
, describe "complement"
|
|
||||||
[ test "complement a positive" <| \() -> Expect.equal -9 (Bitwise.complement 8)
|
|
||||||
, test "complement a negative" <| \() -> Expect.equal 278 (Bitwise.complement -279)
|
|
||||||
]
|
|
||||||
, describe "shiftLeftBy"
|
|
||||||
[ test "8 |> shiftLeftBy 1 == 16" <| \() -> Expect.equal 16 (8 |> Bitwise.shiftLeftBy 1)
|
|
||||||
, test "8 |> shiftLeftby 2 == 32" <| \() -> Expect.equal 32 (8 |> Bitwise.shiftLeftBy 2)
|
|
||||||
]
|
|
||||||
, describe "shiftRightBy"
|
|
||||||
[ test "32 |> shiftRight 1 == 16" <| \() -> Expect.equal 16 (32 |> Bitwise.shiftRightBy 1)
|
|
||||||
, test "32 |> shiftRight 2 == 8" <| \() -> Expect.equal 8 (32 |> Bitwise.shiftRightBy 2)
|
|
||||||
, test "-32 |> shiftRight 1 == -16" <| \() -> Expect.equal -16 (-32 |> Bitwise.shiftRightBy 1)
|
|
||||||
]
|
|
||||||
, describe "shiftRightZfBy"
|
|
||||||
[ test "32 |> shiftRightZfBy 1 == 16" <| \() -> Expect.equal 16 (32 |> Bitwise.shiftRightZfBy 1)
|
|
||||||
, test "32 |> shiftRightZfBy 2 == 8" <| \() -> Expect.equal 8 (32 |> Bitwise.shiftRightZfBy 2)
|
|
||||||
, test "-32 |> shiftRightZfBy 1 == 2147483632" <| \() -> Expect.equal 2147483632 (-32 |> Bitwise.shiftRightZfBy 1)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
@@ -1,113 +0,0 @@
|
|||||||
module Test.Char exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Char exposing (..)
|
|
||||||
import List
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
lower =
|
|
||||||
[ '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' ]
|
|
||||||
|
|
||||||
|
|
||||||
upper =
|
|
||||||
[ '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' ]
|
|
||||||
|
|
||||||
|
|
||||||
dec =
|
|
||||||
[ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9' ]
|
|
||||||
|
|
||||||
|
|
||||||
oct =
|
|
||||||
List.take 8 dec
|
|
||||||
|
|
||||||
|
|
||||||
hexLower =
|
|
||||||
List.take 6 lower
|
|
||||||
|
|
||||||
|
|
||||||
hexUpper =
|
|
||||||
List.take 6 upper
|
|
||||||
|
|
||||||
|
|
||||||
hex =
|
|
||||||
List.append hexLower hexUpper |> List.append dec
|
|
||||||
|
|
||||||
|
|
||||||
lowerCodes =
|
|
||||||
List.range 97 (97 + List.length lower - 1)
|
|
||||||
|
|
||||||
|
|
||||||
upperCodes =
|
|
||||||
List.range 65 (65 + List.length upper - 1)
|
|
||||||
|
|
||||||
|
|
||||||
decCodes =
|
|
||||||
List.range 48 (48 + List.length dec - 1)
|
|
||||||
|
|
||||||
|
|
||||||
oneOf : List a -> a -> Bool
|
|
||||||
oneOf =
|
|
||||||
flip List.member
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
describe "Char"
|
|
||||||
[ describe "toCode"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (lowerCodes) (List.map toCode lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (upperCodes) (List.map toCode upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (decCodes) (List.map toCode dec)
|
|
||||||
]
|
|
||||||
, describe "fromCode"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (lower) (List.map fromCode lowerCodes)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (upper) (List.map fromCode upperCodes)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (dec) (List.map fromCode decCodes)
|
|
||||||
]
|
|
||||||
, describe "toLocaleLower"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (lower) (List.map toLocaleLower lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (lower) (List.map toLocaleLower upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (dec) (List.map toLocaleLower dec)
|
|
||||||
]
|
|
||||||
, describe "toLocaleUpper"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (upper) (List.map toLocaleUpper lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (upper) (List.map toLocaleUpper upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (dec) (List.map toLocaleUpper dec)
|
|
||||||
]
|
|
||||||
, describe "toLower"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (lower) (List.map toLower lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (lower) (List.map toLower upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (dec) (List.map toLower dec)
|
|
||||||
]
|
|
||||||
, describe "toUpper"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (upper) (List.map toUpper lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (upper) (List.map toUpper upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (dec) (List.map toUpper dec)
|
|
||||||
]
|
|
||||||
, describe "isLower"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (True) (List.all isLower lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (False) (List.any isLower upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (False) (List.any isLower dec)
|
|
||||||
]
|
|
||||||
, describe "isUpper"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (False) (List.any isUpper lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (True) (List.all isUpper upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (False) (List.any isUpper dec)
|
|
||||||
]
|
|
||||||
, describe "isDigit"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (False) (List.any isDigit lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (False) (List.any isDigit upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (True) (List.all isDigit dec)
|
|
||||||
]
|
|
||||||
, describe "isHexDigit"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (List.map (oneOf hex) lower) (List.map isHexDigit lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (List.map (oneOf hex) upper) (List.map isHexDigit upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (True) (List.all isHexDigit dec)
|
|
||||||
]
|
|
||||||
, describe "isOctDigit"
|
|
||||||
[ test "a-z" <| \() -> Expect.equal (False) (List.any isOctDigit lower)
|
|
||||||
, test "A-Z" <| \() -> Expect.equal (False) (List.any isOctDigit upper)
|
|
||||||
, test "0-9" <| \() -> Expect.equal (List.map (oneOf oct) dec) (List.map isOctDigit dec)
|
|
||||||
]
|
|
||||||
]
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
module Test.CodeGen exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
import Maybe
|
|
||||||
import Maybe exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
type Wrapper a
|
|
||||||
= Wrapper a
|
|
||||||
|
|
||||||
|
|
||||||
caseUnderscore : Maybe number -> number
|
|
||||||
caseUnderscore m_ =
|
|
||||||
case m_ of
|
|
||||||
Just x ->
|
|
||||||
x
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
0
|
|
||||||
|
|
||||||
|
|
||||||
patternUnderscore : number
|
|
||||||
patternUnderscore =
|
|
||||||
case Just 42 of
|
|
||||||
Just x_ ->
|
|
||||||
x_
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
0
|
|
||||||
|
|
||||||
|
|
||||||
letQualified : number
|
|
||||||
letQualified =
|
|
||||||
let
|
|
||||||
(Wrapper x) =
|
|
||||||
Wrapper 42
|
|
||||||
in
|
|
||||||
x
|
|
||||||
|
|
||||||
|
|
||||||
caseQualified : number
|
|
||||||
caseQualified =
|
|
||||||
case Just 42 of
|
|
||||||
Maybe.Just x ->
|
|
||||||
x
|
|
||||||
|
|
||||||
Nothing ->
|
|
||||||
0
|
|
||||||
|
|
||||||
|
|
||||||
caseScope : String
|
|
||||||
caseScope =
|
|
||||||
case "Not this one!" of
|
|
||||||
string ->
|
|
||||||
case "Hi" of
|
|
||||||
string ->
|
|
||||||
string
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
-- We don't strictly speaking need annotations in this let-expression,
|
|
||||||
-- but having these here exercises the parser to avoid regressions like
|
|
||||||
-- https://github.com/elm-lang/elm-compiler/issues/1535
|
|
||||||
underscores : Test
|
|
||||||
underscores =
|
|
||||||
describe "Underscores"
|
|
||||||
[ test "case" <| \() -> Expect.equal 42 (caseUnderscore (Just 42))
|
|
||||||
, test "pattern" <| \() -> Expect.equal 42 patternUnderscore
|
|
||||||
]
|
|
||||||
|
|
||||||
qualifiedPatterns : Test
|
|
||||||
qualifiedPatterns =
|
|
||||||
describe "Qualified Patterns"
|
|
||||||
[ test "let" <| \() -> Expect.equal 42 letQualified
|
|
||||||
, test "case" <| \() -> Expect.equal 42 caseQualified
|
|
||||||
]
|
|
||||||
|
|
||||||
scope : Test
|
|
||||||
scope =
|
|
||||||
describe "Scoping"
|
|
||||||
[ test "case" <| \() -> Expect.equal "Hi" caseScope ]
|
|
||||||
|
|
||||||
hex : Test
|
|
||||||
hex =
|
|
||||||
describe "Hex"
|
|
||||||
[ test "0xFFFFFFFF" <|
|
|
||||||
\() ->
|
|
||||||
0xFFFFFFFF
|
|
||||||
|> Expect.equal 4294967295
|
|
||||||
, test "0xD066F00D" <|
|
|
||||||
\() ->
|
|
||||||
0xD066F00D
|
|
||||||
|> Expect.equal 3496407053
|
|
||||||
, test "0x00" <|
|
|
||||||
\() ->
|
|
||||||
0x00
|
|
||||||
|> Expect.equal 0
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "CodeGen"
|
|
||||||
[ underscores
|
|
||||||
, qualifiedPatterns
|
|
||||||
, scope
|
|
||||||
, hex
|
|
||||||
]
|
|
||||||
@@ -1,107 +0,0 @@
|
|||||||
module Test.Dict exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Dict
|
|
||||||
import List
|
|
||||||
import Maybe exposing (..)
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
animals : Dict.Dict String String
|
|
||||||
animals =
|
|
||||||
Dict.fromList [ ( "Tom", "cat" ), ( "Jerry", "mouse" ) ]
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
buildTests =
|
|
||||||
describe "build Tests"
|
|
||||||
[ test "empty" <| \() -> Expect.equal (Dict.fromList []) (Dict.empty)
|
|
||||||
, test "singleton" <| \() -> Expect.equal (Dict.fromList [ ( "k", "v" ) ]) (Dict.singleton "k" "v")
|
|
||||||
, test "insert" <| \() -> Expect.equal (Dict.fromList [ ( "k", "v" ) ]) (Dict.insert "k" "v" Dict.empty)
|
|
||||||
, test "insert replace" <| \() -> Expect.equal (Dict.fromList [ ( "k", "vv" ) ]) (Dict.insert "k" "vv" (Dict.singleton "k" "v"))
|
|
||||||
, test "update" <| \() -> Expect.equal (Dict.fromList [ ( "k", "vv" ) ]) (Dict.update "k" (\v -> Just "vv") (Dict.singleton "k" "v"))
|
|
||||||
, test "update Nothing" <| \() -> Expect.equal Dict.empty (Dict.update "k" (\v -> Nothing) (Dict.singleton "k" "v"))
|
|
||||||
, test "remove" <| \() -> Expect.equal Dict.empty (Dict.remove "k" (Dict.singleton "k" "v"))
|
|
||||||
, test "remove not found" <| \() -> Expect.equal (Dict.singleton "k" "v") (Dict.remove "kk" (Dict.singleton "k" "v"))
|
|
||||||
]
|
|
||||||
|
|
||||||
queryTests =
|
|
||||||
describe "query Tests"
|
|
||||||
[ test "member 1" <| \() -> Expect.equal True (Dict.member "Tom" animals)
|
|
||||||
, test "member 2" <| \() -> Expect.equal False (Dict.member "Spike" animals)
|
|
||||||
, test "get 1" <| \() -> Expect.equal (Just "cat") (Dict.get "Tom" animals)
|
|
||||||
, test "get 2" <| \() -> Expect.equal Nothing (Dict.get "Spike" animals)
|
|
||||||
, test "size of empty dictionary" <| \() -> Expect.equal 0 (Dict.size Dict.empty)
|
|
||||||
, test "size of example dictionary" <| \() -> Expect.equal 2 (Dict.size animals)
|
|
||||||
]
|
|
||||||
|
|
||||||
combineTests =
|
|
||||||
describe "combine Tests"
|
|
||||||
[ test "union" <| \() -> Expect.equal animals (Dict.union (Dict.singleton "Jerry" "mouse") (Dict.singleton "Tom" "cat"))
|
|
||||||
, test "union collison" <| \() -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.union (Dict.singleton "Tom" "cat") (Dict.singleton "Tom" "mouse"))
|
|
||||||
, test "intersect" <| \() -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.intersect animals (Dict.singleton "Tom" "cat"))
|
|
||||||
, test "diff" <| \() -> Expect.equal (Dict.singleton "Jerry" "mouse") (Dict.diff animals (Dict.singleton "Tom" "cat"))
|
|
||||||
]
|
|
||||||
|
|
||||||
transformTests =
|
|
||||||
describe "transform Tests"
|
|
||||||
[ test "filter" <| \() -> Expect.equal (Dict.singleton "Tom" "cat") (Dict.filter (\k v -> k == "Tom") animals)
|
|
||||||
, test "partition" <| \() -> Expect.equal ( Dict.singleton "Tom" "cat", Dict.singleton "Jerry" "mouse" ) (Dict.partition (\k v -> k == "Tom") animals)
|
|
||||||
]
|
|
||||||
|
|
||||||
mergeTests =
|
|
||||||
let
|
|
||||||
insertBoth key leftVal rightVal dict =
|
|
||||||
Dict.insert key (leftVal ++ rightVal) dict
|
|
||||||
|
|
||||||
s1 =
|
|
||||||
Dict.empty |> Dict.insert "u1" [ 1 ]
|
|
||||||
|
|
||||||
s2 =
|
|
||||||
Dict.empty |> Dict.insert "u2" [ 2 ]
|
|
||||||
|
|
||||||
s23 =
|
|
||||||
Dict.empty |> Dict.insert "u2" [ 3 ]
|
|
||||||
|
|
||||||
b1 =
|
|
||||||
List.map (\i -> ( i, [ i ] )) (List.range 1 10) |> Dict.fromList
|
|
||||||
|
|
||||||
b2 =
|
|
||||||
List.map (\i -> ( i, [ i ] )) (List.range 5 15) |> Dict.fromList
|
|
||||||
|
|
||||||
bExpected =
|
|
||||||
[ ( 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 ] ) ]
|
|
||||||
in
|
|
||||||
describe "merge Tests"
|
|
||||||
[ test "merge empties" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal (Dict.empty)
|
|
||||||
(Dict.merge Dict.insert insertBoth Dict.insert Dict.empty Dict.empty Dict.empty)
|
|
||||||
, test "merge singletons in order" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal [ ( "u1", [ 1 ] ), ( "u2", [ 2 ] ) ]
|
|
||||||
((Dict.merge Dict.insert insertBoth Dict.insert s1 s2 Dict.empty) |> Dict.toList)
|
|
||||||
, test "merge singletons out of order" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal [ ( "u1", [ 1 ] ), ( "u2", [ 2 ] ) ]
|
|
||||||
((Dict.merge Dict.insert insertBoth Dict.insert s2 s1 Dict.empty) |> Dict.toList)
|
|
||||||
, test "merge with duplicate key" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal [ ( "u2", [ 2, 3 ] ) ]
|
|
||||||
((Dict.merge Dict.insert insertBoth Dict.insert s2 s23 Dict.empty) |> Dict.toList)
|
|
||||||
, test "partially overlapping" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal bExpected
|
|
||||||
((Dict.merge Dict.insert insertBoth Dict.insert b1 b2 Dict.empty) |> Dict.toList)
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "Dict Tests"
|
|
||||||
[ buildTests
|
|
||||||
, queryTests
|
|
||||||
, combineTests
|
|
||||||
, transformTests
|
|
||||||
, mergeTests
|
|
||||||
]
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
module Test.Equality exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Maybe exposing (..)
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
type Different
|
|
||||||
= A String
|
|
||||||
| B (List Int)
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
diffTests =
|
|
||||||
describe "ADT equality"
|
|
||||||
[ test "As eq" <| \() -> Expect.equal True (A "a" == A "a")
|
|
||||||
, test "Bs eq" <| \() -> Expect.equal True (B [ 1 ] == B [ 1 ])
|
|
||||||
, test "A left neq" <| \() -> Expect.equal True (A "a" /= B [ 1 ])
|
|
||||||
, test "A left neq" <| \() -> Expect.equal True (B [ 1 ] /= A "a")
|
|
||||||
]
|
|
||||||
|
|
||||||
recordTests =
|
|
||||||
describe "Record equality"
|
|
||||||
[ test "empty same" <| \() -> Expect.equal True ({} == {})
|
|
||||||
, test "ctor same" <| \() -> Expect.equal True ({ field = Just 3 } == { field = Just 3 })
|
|
||||||
, test "ctor same, special case" <| \() -> Expect.equal True ({ ctor = Just 3 } == { ctor = Just 3 })
|
|
||||||
, test "ctor diff" <| \() -> Expect.equal True ({ field = Just 3 } /= { field = Nothing })
|
|
||||||
, test "ctor diff, special case" <| \() -> Expect.equal True ({ ctor = Just 3 } /= { ctor = Nothing })
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "Equality Tests" [ diffTests, recordTests ]
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
module Test.Json exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Result exposing (..)
|
|
||||||
import Json.Decode as Json
|
|
||||||
import String
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
describe "Json decode"
|
|
||||||
[ intTests
|
|
||||||
, customTests
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
intTests : Test
|
|
||||||
intTests =
|
|
||||||
let
|
|
||||||
testInt val str =
|
|
||||||
case Json.decodeString Json.int str of
|
|
||||||
Ok _ ->
|
|
||||||
Expect.equal val True
|
|
||||||
|
|
||||||
Err _ ->
|
|
||||||
Expect.equal val False
|
|
||||||
in
|
|
||||||
describe "Json decode int"
|
|
||||||
[ test "whole int" <| \() -> testInt True "4"
|
|
||||||
, test "-whole int" <| \() -> testInt True "-4"
|
|
||||||
, test "whole float" <| \() -> testInt True "4.0"
|
|
||||||
, test "-whole float" <| \() -> testInt True "-4.0"
|
|
||||||
, test "large int" <| \() -> testInt True "1801439850948"
|
|
||||||
, test "-large int" <| \() -> testInt True "-1801439850948"
|
|
||||||
, test "float" <| \() -> testInt False "4.2"
|
|
||||||
, test "-float" <| \() -> testInt False "-4.2"
|
|
||||||
, test "Infinity" <| \() -> testInt False "Infinity"
|
|
||||||
, test "-Infinity" <| \() -> testInt False "-Infinity"
|
|
||||||
, test "NaN" <| \() -> testInt False "NaN"
|
|
||||||
, test "-NaN" <| \() -> testInt False "-NaN"
|
|
||||||
, test "true" <| \() -> testInt False "true"
|
|
||||||
, test "false" <| \() -> testInt False "false"
|
|
||||||
, test "string" <| \() -> testInt False "\"string\""
|
|
||||||
, test "object" <| \() -> testInt False "{}"
|
|
||||||
, test "null" <| \() -> testInt False "null"
|
|
||||||
, test "undefined" <| \() -> testInt False "undefined"
|
|
||||||
, test "Decoder expects object finds array, was crashing runtime." <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Err "Expecting an object but instead got: []")
|
|
||||||
(Json.decodeString (Json.dict Json.float) "[]")
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
customTests : Test
|
|
||||||
customTests =
|
|
||||||
let
|
|
||||||
jsonString =
|
|
||||||
"""{ "foo": "bar" }"""
|
|
||||||
|
|
||||||
customErrorMessage =
|
|
||||||
"I want to see this message!"
|
|
||||||
|
|
||||||
myDecoder =
|
|
||||||
Json.field "foo" Json.string |> Json.andThen (\_ -> Json.fail customErrorMessage)
|
|
||||||
|
|
||||||
assertion =
|
|
||||||
case Json.decodeString myDecoder jsonString of
|
|
||||||
Ok _ ->
|
|
||||||
Expect.fail "expected `customDecoder` to produce a value of type Err, but got Ok"
|
|
||||||
|
|
||||||
Err message ->
|
|
||||||
if String.contains customErrorMessage message then
|
|
||||||
Expect.pass
|
|
||||||
else
|
|
||||||
Expect.fail <|
|
|
||||||
"expected `customDecoder` to preserve user's error message '"
|
|
||||||
++ customErrorMessage
|
|
||||||
++ "', but instead got: "
|
|
||||||
++ message
|
|
||||||
in
|
|
||||||
test "customDecoder preserves user error messages" <| \() -> assertion
|
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
module Test.List exposing (tests)
|
|
||||||
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Maybe exposing (Maybe(Nothing, Just))
|
|
||||||
import List exposing (..)
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
describe "List Tests"
|
|
||||||
[ testListOfN 0
|
|
||||||
, testListOfN 1
|
|
||||||
, testListOfN 2
|
|
||||||
, testListOfN 5000
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
testListOfN : Int -> Test
|
|
||||||
testListOfN n =
|
|
||||||
let
|
|
||||||
xs =
|
|
||||||
List.range 1 n
|
|
||||||
|
|
||||||
xsOpp =
|
|
||||||
List.range -n -1
|
|
||||||
|
|
||||||
xsNeg =
|
|
||||||
foldl (::) [] xsOpp
|
|
||||||
|
|
||||||
-- assume foldl and (::) work
|
|
||||||
zs =
|
|
||||||
List.range 0 n
|
|
||||||
|
|
||||||
sumSeq k =
|
|
||||||
k * (k + 1) // 2
|
|
||||||
|
|
||||||
xsSum =
|
|
||||||
sumSeq n
|
|
||||||
|
|
||||||
mid =
|
|
||||||
n // 2
|
|
||||||
in
|
|
||||||
describe (toString n ++ " elements")
|
|
||||||
[ describe "foldl"
|
|
||||||
[ test "order" <| \() -> Expect.equal (n) (foldl (\x acc -> x) 0 xs)
|
|
||||||
, test "total" <| \() -> Expect.equal (xsSum) (foldl (+) 0 xs)
|
|
||||||
]
|
|
||||||
, describe "foldr"
|
|
||||||
[ test "order" <| \() -> Expect.equal (min 1 n) (foldr (\x acc -> x) 0 xs)
|
|
||||||
, test "total" <| \() -> Expect.equal (xsSum) (foldl (+) 0 xs)
|
|
||||||
]
|
|
||||||
, describe "map"
|
|
||||||
[ test "identity" <| \() -> Expect.equal (xs) (map identity xs)
|
|
||||||
, test "linear" <| \() -> Expect.equal (List.range 2 (n + 1)) (map ((+) 1) xs)
|
|
||||||
]
|
|
||||||
, test "isEmpty" <| \() -> Expect.equal (n == 0) (isEmpty xs)
|
|
||||||
, test "length" <| \() -> Expect.equal (n) (length xs)
|
|
||||||
, test "reverse" <| \() -> Expect.equal (xsOpp) (reverse xsNeg)
|
|
||||||
, describe "member"
|
|
||||||
[ test "positive" <| \() -> Expect.equal (True) (member n zs)
|
|
||||||
, test "negative" <| \() -> Expect.equal (False) (member (n + 1) xs)
|
|
||||||
]
|
|
||||||
, test "head" <|
|
|
||||||
\() ->
|
|
||||||
if n == 0 then
|
|
||||||
Expect.equal (Nothing) (head xs)
|
|
||||||
else
|
|
||||||
Expect.equal (Just 1) (head xs)
|
|
||||||
, describe "List.filter"
|
|
||||||
[ test "none" <| \() -> Expect.equal ([]) (List.filter (\x -> x > n) xs)
|
|
||||||
, test "one" <| \() -> Expect.equal ([ n ]) (List.filter (\z -> z == n) zs)
|
|
||||||
, test "all" <| \() -> Expect.equal (xs) (List.filter (\x -> x <= n) xs)
|
|
||||||
]
|
|
||||||
, describe "take"
|
|
||||||
[ test "none" <| \() -> Expect.equal ([]) (take 0 xs)
|
|
||||||
, test "some" <| \() -> Expect.equal (List.range 0 (n - 1)) (take n zs)
|
|
||||||
, test "all" <| \() -> Expect.equal (xs) (take n xs)
|
|
||||||
, test "all+" <| \() -> Expect.equal (xs) (take (n + 1) xs)
|
|
||||||
]
|
|
||||||
, describe "drop"
|
|
||||||
[ test "none" <| \() -> Expect.equal (xs) (drop 0 xs)
|
|
||||||
, test "some" <| \() -> Expect.equal ([ n ]) (drop n zs)
|
|
||||||
, test "all" <| \() -> Expect.equal ([]) (drop n xs)
|
|
||||||
, test "all+" <| \() -> Expect.equal ([]) (drop (n + 1) xs)
|
|
||||||
]
|
|
||||||
, test "repeat" <| \() -> Expect.equal (map (\x -> -1) xs) (repeat n -1)
|
|
||||||
, test "append" <| \() -> Expect.equal (xsSum * 2) (append xs xs |> foldl (+) 0)
|
|
||||||
, test "(::)" <| \() -> Expect.equal (append [ -1 ] xs) (-1 :: xs)
|
|
||||||
, test "List.concat" <| \() -> Expect.equal (append xs (append zs xs)) (List.concat [ xs, zs, xs ])
|
|
||||||
, test "intersperse" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
( min -(n - 1) 0, xsSum )
|
|
||||||
(intersperse -1 xs |> foldl (\x ( c1, c2 ) -> ( c2, c1 + x )) ( 0, 0 ))
|
|
||||||
, describe "partition"
|
|
||||||
[ test "left" <| \() -> Expect.equal ( xs, [] ) (partition (\x -> x > 0) xs)
|
|
||||||
, test "right" <| \() -> Expect.equal ( [], xs ) (partition (\x -> x < 0) xs)
|
|
||||||
, test "split" <| \() -> Expect.equal ( List.range (mid + 1) n, List.range 1 mid ) (partition (\x -> x > mid) xs)
|
|
||||||
]
|
|
||||||
, describe "map2"
|
|
||||||
[ test "same length" <| \() -> Expect.equal (map ((*) 2) xs) (map2 (+) xs xs)
|
|
||||||
, test "long first" <| \() -> Expect.equal (map (\x -> x * 2 - 1) xs) (map2 (+) zs xs)
|
|
||||||
, test "short first" <| \() -> Expect.equal (map (\x -> x * 2 - 1) xs) (map2 (+) xs zs)
|
|
||||||
]
|
|
||||||
, test "unzip" <| \() -> Expect.equal ( xsNeg, xs ) (map (\x -> ( -x, x )) xs |> unzip)
|
|
||||||
, describe "filterMap"
|
|
||||||
[ test "none" <| \() -> Expect.equal ([]) (filterMap (\x -> Nothing) xs)
|
|
||||||
, test "all" <| \() -> Expect.equal (xsNeg) (filterMap (\x -> Just -x) xs)
|
|
||||||
, let
|
|
||||||
halve x =
|
|
||||||
if x % 2 == 0 then
|
|
||||||
Just (x // 2)
|
|
||||||
else
|
|
||||||
Nothing
|
|
||||||
in
|
|
||||||
test "some" <| \() -> Expect.equal (List.range 1 mid) (filterMap halve xs)
|
|
||||||
]
|
|
||||||
, describe "concatMap"
|
|
||||||
[ test "none" <| \() -> Expect.equal ([]) (concatMap (\x -> []) xs)
|
|
||||||
, test "all" <| \() -> Expect.equal (xsNeg) (concatMap (\x -> [ -x ]) xs)
|
|
||||||
]
|
|
||||||
, test "indexedMap" <| \() -> Expect.equal (map2 (,) zs xsNeg) (indexedMap (\i x -> ( i, -x )) xs)
|
|
||||||
, test "sum" <| \() -> Expect.equal (xsSum) (sum xs)
|
|
||||||
, test "product" <| \() -> Expect.equal (0) (product zs)
|
|
||||||
, test "maximum" <|
|
|
||||||
\() ->
|
|
||||||
if n == 0 then
|
|
||||||
Expect.equal (Nothing) (maximum xs)
|
|
||||||
else
|
|
||||||
Expect.equal (Just n) (maximum xs)
|
|
||||||
, test "minimum" <|
|
|
||||||
\() ->
|
|
||||||
if n == 0 then
|
|
||||||
Expect.equal (Nothing) (minimum xs)
|
|
||||||
else
|
|
||||||
Expect.equal (Just 1) (minimum xs)
|
|
||||||
, describe "all"
|
|
||||||
[ test "false" <| \() -> Expect.equal (False) (all (\z -> z < n) zs)
|
|
||||||
, test "true" <| \() -> Expect.equal (True) (all (\x -> x <= n) xs)
|
|
||||||
]
|
|
||||||
, describe "any"
|
|
||||||
[ test "false" <| \() -> Expect.equal (False) (any (\x -> x > n) xs)
|
|
||||||
, test "true" <| \() -> Expect.equal (True) (any (\z -> z >= n) zs)
|
|
||||||
]
|
|
||||||
, describe "sort"
|
|
||||||
[ test "sorted" <| \() -> Expect.equal (xs) (sort xs)
|
|
||||||
, test "unsorted" <| \() -> Expect.equal (xsOpp) (sort xsNeg)
|
|
||||||
]
|
|
||||||
, describe "sortBy"
|
|
||||||
[ test "sorted" <| \() -> Expect.equal (xsNeg) (sortBy negate xsNeg)
|
|
||||||
, test "unsorted" <| \() -> Expect.equal (xsNeg) (sortBy negate xsOpp)
|
|
||||||
]
|
|
||||||
, describe "sortWith"
|
|
||||||
[ test "sorted" <| \() -> Expect.equal (xsNeg) (sortWith (flip compare) xsNeg)
|
|
||||||
, test "unsorted" <| \() -> Expect.equal (xsNeg) (sortWith (flip compare) xsOpp)
|
|
||||||
]
|
|
||||||
, test "scanl" <| \() -> Expect.equal (0 :: map sumSeq xs) (scanl (+) 0 xs)
|
|
||||||
]
|
|
||||||
@@ -1,169 +0,0 @@
|
|||||||
module Test.Maybe exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Maybe exposing (..)
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
describe "Maybe Tests"
|
|
||||||
|
|
||||||
[ describe "Common Helpers Tests"
|
|
||||||
|
|
||||||
[ describe "withDefault Tests"
|
|
||||||
[ test "no default used" <|
|
|
||||||
\() -> Expect.equal 0 (Maybe.withDefault 5 (Just 0))
|
|
||||||
, test "default used" <|
|
|
||||||
\() -> Expect.equal 5 (Maybe.withDefault 5 (Nothing))
|
|
||||||
]
|
|
||||||
|
|
||||||
, describe "map Tests"
|
|
||||||
( let f = (\n -> n + 1) in
|
|
||||||
[ test "on Just" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Just 1)
|
|
||||||
(Maybe.map f (Just 0))
|
|
||||||
, test "on Nothing" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map f Nothing)
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
, describe "map2 Tests"
|
|
||||||
( let f = (+) in
|
|
||||||
[ test "on (Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Just 1)
|
|
||||||
(Maybe.map2 f (Just 0) (Just 1))
|
|
||||||
, test "on (Just, Nothing)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map2 f (Just 0) Nothing)
|
|
||||||
, test "on (Nothing, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map2 f Nothing (Just 0))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
, describe "map3 Tests"
|
|
||||||
( let f = (\a b c -> a + b + c) in
|
|
||||||
[ test "on (Just, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Just 3)
|
|
||||||
(Maybe.map3 f (Just 1) (Just 1) (Just 1))
|
|
||||||
, test "on (Just, Just, Nothing)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map3 f (Just 1) (Just 1) Nothing)
|
|
||||||
, test "on (Just, Nothing, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map3 f (Just 1) Nothing (Just 1))
|
|
||||||
, test "on (Nothing, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map3 f Nothing (Just 1) (Just 1))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
, describe "map4 Tests"
|
|
||||||
( let f = (\a b c d -> a + b + c + d) in
|
|
||||||
[ test "on (Just, Just, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Just 4)
|
|
||||||
(Maybe.map4 f (Just 1) (Just 1) (Just 1) (Just 1))
|
|
||||||
, test "on (Just, Just, Just, Nothing)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map4 f (Just 1) (Just 1) (Just 1) Nothing)
|
|
||||||
, test "on (Just, Just, Nothing, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map4 f (Just 1) (Just 1) Nothing (Just 1))
|
|
||||||
, test "on (Just, Nothing, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map4 f (Just 1) Nothing (Just 1) (Just 1))
|
|
||||||
, test "on (Nothing, Just, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map4 f Nothing (Just 1) (Just 1) (Just 1))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
, describe "map5 Tests"
|
|
||||||
( let f = (\a b c d e -> a + b + c + d + e) in
|
|
||||||
[ test "on (Just, Just, Just, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Just 5)
|
|
||||||
(Maybe.map5 f (Just 1) (Just 1) (Just 1) (Just 1) (Just 1))
|
|
||||||
, test "on (Just, Just, Just, Just, Nothing)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map5 f (Just 1) (Just 1) (Just 1) (Just 1) Nothing)
|
|
||||||
, test "on (Just, Just, Just, Nothing, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map5 f (Just 1) (Just 1) (Just 1) Nothing (Just 1))
|
|
||||||
, test "on (Just, Just, Nothing, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map5 f (Just 1) (Just 1) Nothing (Just 1) (Just 1))
|
|
||||||
, test "on (Just, Nothing, Just, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map5 f (Just 1) Nothing (Just 1) (Just 1) (Just 1))
|
|
||||||
, test "on (Nothing, Just, Just, Just, Just)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.map5 f Nothing (Just 1) (Just 1) (Just 1) (Just 1))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|
|
||||||
]
|
|
||||||
|
|
||||||
, describe "Chaining Maybes Tests"
|
|
||||||
|
|
||||||
[ describe "andThen Tests"
|
|
||||||
[ test "succeeding chain" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Just 1)
|
|
||||||
(Maybe.andThen (\a -> Just a) (Just 1))
|
|
||||||
, test "failing chain (original Maybe failed)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.andThen (\a -> Just a) Nothing)
|
|
||||||
, test "failing chain (chained function failed)" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
Nothing
|
|
||||||
(Maybe.andThen (\a -> Nothing) (Just 1))
|
|
||||||
]
|
|
||||||
]
|
|
||||||
|
|
||||||
]
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
module Test.Regex exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Regex exposing (..)
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
simpleTests =
|
|
||||||
describe "Simple Stuff"
|
|
||||||
[ test "split All" <| \() -> Expect.equal [ "a", "b" ] (split All (regex ",") "a,b")
|
|
||||||
, test "split" <| \() -> Expect.equal [ "a", "b,c" ] (split (AtMost 1) (regex ",") "a,b,c")
|
|
||||||
, test "split idempotent" <|
|
|
||||||
\() ->
|
|
||||||
let
|
|
||||||
findComma =
|
|
||||||
regex ","
|
|
||||||
in
|
|
||||||
Expect.equal
|
|
||||||
(split (AtMost 1) findComma "a,b,c,d,e")
|
|
||||||
(split (AtMost 1) findComma "a,b,c,d,e")
|
|
||||||
, test "find All" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
([ Match "a" [] 0 1, Match "b" [] 1 2 ])
|
|
||||||
(find All (regex ".") "ab")
|
|
||||||
, test "find All" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
([ Match "" [] 0 1 ])
|
|
||||||
(find All (regex ".*") "")
|
|
||||||
, test "replace AtMost 0" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal "The quick brown fox"
|
|
||||||
(replace (AtMost 0) (regex "[aeiou]") (\_ -> "") "The quick brown fox")
|
|
||||||
, test "replace AtMost 1" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal "Th quick brown fox"
|
|
||||||
(replace (AtMost 1) (regex "[aeiou]") (\_ -> "") "The quick brown fox")
|
|
||||||
, test "replace AtMost 2" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal "Th qick brown fox"
|
|
||||||
(replace (AtMost 2) (regex "[aeiou]") (\_ -> "") "The quick brown fox")
|
|
||||||
, test "replace All" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal "Th qck brwn fx"
|
|
||||||
(replace All (regex "[aeiou]") (\_ -> "") "The quick brown fox")
|
|
||||||
, test "replace using index" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal "a1b3c"
|
|
||||||
(replace All (regex ",") (\match -> toString match.index) "a,b,c")
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "Regex" [ simpleTests ]
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
module Test.Result exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Result
|
|
||||||
import Result exposing (Result(..))
|
|
||||||
import String
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
isEven n =
|
|
||||||
if n % 2 == 0 then
|
|
||||||
Ok n
|
|
||||||
else
|
|
||||||
Err "number is odd"
|
|
||||||
|
|
||||||
|
|
||||||
add3 a b c =
|
|
||||||
a + b + c
|
|
||||||
|
|
||||||
|
|
||||||
add4 a b c d =
|
|
||||||
a + b + c + d
|
|
||||||
|
|
||||||
|
|
||||||
add5 a b c d e =
|
|
||||||
a + b + c + d + e
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
mapTests =
|
|
||||||
describe "map Tests"
|
|
||||||
[ test "map Ok" <| \() -> Expect.equal (Ok 3) (Result.map ((+) 1) (Ok 2))
|
|
||||||
, test "map Err" <| \() -> Expect.equal (Err "error") (Result.map ((+) 1) (Err "error"))
|
|
||||||
]
|
|
||||||
|
|
||||||
mapNTests =
|
|
||||||
describe "mapN Tests"
|
|
||||||
[ test "map2 Ok" <| \() -> Expect.equal (Ok 3) (Result.map2 (+) (Ok 1) (Ok 2))
|
|
||||||
, test "map2 Err" <| \() -> Expect.equal (Err "x") (Result.map2 (+) (Ok 1) (Err "x"))
|
|
||||||
, test "map3 Ok" <| \() -> Expect.equal (Ok 6) (Result.map3 add3 (Ok 1) (Ok 2) (Ok 3))
|
|
||||||
, test "map3 Err" <| \() -> Expect.equal (Err "x") (Result.map3 add3 (Ok 1) (Ok 2) (Err "x"))
|
|
||||||
, test "map4 Ok" <| \() -> Expect.equal (Ok 10) (Result.map4 add4 (Ok 1) (Ok 2) (Ok 3) (Ok 4))
|
|
||||||
, test "map4 Err" <| \() -> Expect.equal (Err "x") (Result.map4 add4 (Ok 1) (Ok 2) (Ok 3) (Err "x"))
|
|
||||||
, test "map5 Ok" <| \() -> Expect.equal (Ok 15) (Result.map5 add5 (Ok 1) (Ok 2) (Ok 3) (Ok 4) (Ok 5))
|
|
||||||
, test "map5 Err" <| \() -> Expect.equal (Err "x") (Result.map5 add5 (Ok 1) (Ok 2) (Ok 3) (Ok 4) (Err "x"))
|
|
||||||
]
|
|
||||||
|
|
||||||
andThenTests =
|
|
||||||
describe "andThen Tests"
|
|
||||||
[ test "andThen Ok" <| \() -> Expect.equal (Ok 42) ((String.toInt "42") |> Result.andThen isEven)
|
|
||||||
, test "andThen first Err" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Err "could not convert string '4.2' to an Int")
|
|
||||||
(String.toInt "4.2" |> Result.andThen isEven)
|
|
||||||
, test "andThen second Err" <|
|
|
||||||
\() ->
|
|
||||||
Expect.equal
|
|
||||||
(Err "number is odd")
|
|
||||||
(String.toInt "41" |> Result.andThen isEven)
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "Result Tests"
|
|
||||||
[ mapTests
|
|
||||||
, mapNTests
|
|
||||||
, andThenTests
|
|
||||||
]
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
module Test.Set exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import Set
|
|
||||||
import Set exposing (Set)
|
|
||||||
import List
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
set : Set Int
|
|
||||||
set =
|
|
||||||
Set.fromList <| List.range 1 100
|
|
||||||
|
|
||||||
|
|
||||||
setPart1 : Set Int
|
|
||||||
setPart1 =
|
|
||||||
Set.fromList <| List.range 1 50
|
|
||||||
|
|
||||||
|
|
||||||
setPart2 : Set Int
|
|
||||||
setPart2 =
|
|
||||||
Set.fromList <| List.range 51 100
|
|
||||||
|
|
||||||
|
|
||||||
pred : Int -> Bool
|
|
||||||
pred x =
|
|
||||||
x <= 50
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
queryTests =
|
|
||||||
describe "query Tests"
|
|
||||||
[ test "size of set of 100 elements" <|
|
|
||||||
\() -> Expect.equal 100 (Set.size set)
|
|
||||||
]
|
|
||||||
|
|
||||||
filterTests =
|
|
||||||
describe "filter Tests"
|
|
||||||
[ test "Simple filter" <|
|
|
||||||
\() -> Expect.equal setPart1 <| Set.filter pred set
|
|
||||||
]
|
|
||||||
|
|
||||||
partitionTests =
|
|
||||||
describe "partition Tests"
|
|
||||||
[ test "Simple partition" <|
|
|
||||||
\() -> Expect.equal ( setPart1, setPart2 ) <| Set.partition pred set
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "Set Tests" [ queryTests, partitionTests, filterTests ]
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
module Test.String exposing (tests)
|
|
||||||
|
|
||||||
import Basics exposing (..)
|
|
||||||
import List
|
|
||||||
import Maybe exposing (..)
|
|
||||||
import Result exposing (Result(..))
|
|
||||||
import String
|
|
||||||
import Test exposing (..)
|
|
||||||
import Expect
|
|
||||||
|
|
||||||
|
|
||||||
tests : Test
|
|
||||||
tests =
|
|
||||||
let
|
|
||||||
simpleTests =
|
|
||||||
describe "Simple Stuff"
|
|
||||||
[ test "is empty" <| \() -> Expect.equal True (String.isEmpty "")
|
|
||||||
, test "is not empty" <| \() -> Expect.equal True (not (String.isEmpty ("the world")))
|
|
||||||
, test "length" <| \() -> Expect.equal 11 (String.length "innumerable")
|
|
||||||
, test "endsWith" <| \() -> Expect.equal True <| String.endsWith "ship" "spaceship"
|
|
||||||
, test "reverse" <| \() -> Expect.equal "desserts" (String.reverse "stressed")
|
|
||||||
, test "repeat" <| \() -> Expect.equal "hahaha" (String.repeat 3 "ha")
|
|
||||||
, test "indexes" <| \() -> Expect.equal [ 0, 2 ] (String.indexes "a" "aha")
|
|
||||||
, test "empty indexes" <| \() -> Expect.equal [] (String.indexes "" "aha")
|
|
||||||
]
|
|
||||||
|
|
||||||
combiningTests =
|
|
||||||
describe "Combining Strings"
|
|
||||||
[ test "uncons non-empty" <| \() -> Expect.equal (Just ( 'a', "bc" )) (String.uncons "abc")
|
|
||||||
, test "uncons empty" <| \() -> Expect.equal Nothing (String.uncons "")
|
|
||||||
, test "append 1" <| \() -> Expect.equal "butterfly" (String.append "butter" "fly")
|
|
||||||
, test "append 2" <| \() -> Expect.equal "butter" (String.append "butter" "")
|
|
||||||
, test "append 3" <| \() -> Expect.equal "butter" (String.append "" "butter")
|
|
||||||
, test "concat" <| \() -> Expect.equal "nevertheless" (String.concat [ "never", "the", "less" ])
|
|
||||||
, test "split commas" <| \() -> Expect.equal [ "cat", "dog", "cow" ] (String.split "," "cat,dog,cow")
|
|
||||||
, test "split slashes" <| \() -> Expect.equal [ "home", "steve", "Desktop", "" ] (String.split "/" "home/steve/Desktop/")
|
|
||||||
, test "join spaces" <| \() -> Expect.equal "cat dog cow" (String.join " " [ "cat", "dog", "cow" ])
|
|
||||||
, test "join slashes" <| \() -> Expect.equal "home/steve/Desktop" (String.join "/" [ "home", "steve", "Desktop" ])
|
|
||||||
, test "slice 1" <| \() -> Expect.equal "c" (String.slice 2 3 "abcd")
|
|
||||||
, test "slice 2" <| \() -> Expect.equal "abc" (String.slice 0 3 "abcd")
|
|
||||||
, test "slice 3" <| \() -> Expect.equal "abc" (String.slice 0 -1 "abcd")
|
|
||||||
, test "slice 4" <| \() -> Expect.equal "cd" (String.slice -2 4 "abcd")
|
|
||||||
]
|
|
||||||
|
|
||||||
intTests =
|
|
||||||
describe "String.toInt"
|
|
||||||
[ goodInt "1234" 1234
|
|
||||||
, goodInt "+1234" 1234
|
|
||||||
, goodInt "-1234" -1234
|
|
||||||
, badInt "1.34"
|
|
||||||
, badInt "1e31"
|
|
||||||
, badInt "123a"
|
|
||||||
, goodInt "0123" 123
|
|
||||||
, goodInt "0x001A" 26
|
|
||||||
, goodInt "0x001a" 26
|
|
||||||
, goodInt "0xBEEF" 48879
|
|
||||||
, badInt "0x12.0"
|
|
||||||
, badInt "0x12an"
|
|
||||||
]
|
|
||||||
|
|
||||||
floatTests =
|
|
||||||
describe "String.toFloat"
|
|
||||||
[ goodFloat "123" 123
|
|
||||||
, goodFloat "3.14" 3.14
|
|
||||||
, goodFloat "+3.14" 3.14
|
|
||||||
, goodFloat "-3.14" -3.14
|
|
||||||
, goodFloat "0.12" 0.12
|
|
||||||
, goodFloat ".12" 0.12
|
|
||||||
, goodFloat "1e-42" 1e-42
|
|
||||||
, goodFloat "6.022e23" 6.022e23
|
|
||||||
, goodFloat "6.022E23" 6.022e23
|
|
||||||
, goodFloat "6.022e+23" 6.022e23
|
|
||||||
, badFloat "6.022e"
|
|
||||||
, badFloat "6.022n"
|
|
||||||
, badFloat "6.022.31"
|
|
||||||
]
|
|
||||||
in
|
|
||||||
describe "String" [ simpleTests, combiningTests, intTests, floatTests ]
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- NUMBER HELPERS
|
|
||||||
|
|
||||||
|
|
||||||
goodInt : String -> Int -> Test
|
|
||||||
goodInt str int =
|
|
||||||
test str <| \_ ->
|
|
||||||
Expect.equal (Ok int) (String.toInt str)
|
|
||||||
|
|
||||||
|
|
||||||
badInt : String -> Test
|
|
||||||
badInt str =
|
|
||||||
test str <| \_ ->
|
|
||||||
Expect.equal
|
|
||||||
(Err ("could not convert string '" ++ str ++ "' to an Int"))
|
|
||||||
(String.toInt str)
|
|
||||||
|
|
||||||
|
|
||||||
goodFloat : String -> Float -> Test
|
|
||||||
goodFloat str float =
|
|
||||||
test str <| \_ ->
|
|
||||||
Expect.equal (Ok float) (String.toFloat str)
|
|
||||||
|
|
||||||
|
|
||||||
badFloat : String -> Test
|
|
||||||
badFloat str =
|
|
||||||
test str <| \_ ->
|
|
||||||
Expect.equal
|
|
||||||
(Err ("could not convert string '" ++ str ++ "' to a Float"))
|
|
||||||
(String.toFloat str)
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "1.1.1",
|
|
||||||
"summary": "Tests for Elm's standard libraries",
|
|
||||||
"repository": "http://github.com/elm-lang/core.git",
|
|
||||||
"license": "BSD3",
|
|
||||||
"source-directories": [
|
|
||||||
".",
|
|
||||||
"../src"
|
|
||||||
],
|
|
||||||
"exposed-modules": [ ],
|
|
||||||
"native-modules": true,
|
|
||||||
"dependencies": {
|
|
||||||
"elm-community/elm-test": "3.1.0 <= v < 4.0.0",
|
|
||||||
"rtfeldman/node-test-runner": "3.0.0 <= v < 4.0.0"
|
|
||||||
},
|
|
||||||
"elm-version": "0.18.0 <= v < 0.19.0"
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
#!/bin/sh
|
|
||||||
|
|
||||||
cd "$(dirname "$0")"
|
|
||||||
set -e
|
|
||||||
|
|
||||||
|
|
||||||
elm-package install -y
|
|
||||||
|
|
||||||
VERSION_DIR="$(ls elm-stuff/packages/elm-lang/core/)"
|
|
||||||
CORE_PACKAGE_DIR="elm-stuff/packages/elm-lang/core/$VERSION_DIR"
|
|
||||||
CORE_GIT_DIR="$(dirname $PWD)"
|
|
||||||
|
|
||||||
echo "Linking $CORE_PACKAGE_DIR to $CORE_GIT_DIR"
|
|
||||||
rm -rf $CORE_PACKAGE_DIR
|
|
||||||
ln -s $CORE_GIT_DIR $CORE_PACKAGE_DIR
|
|
||||||
|
|
||||||
elm-make --yes --output test.js Main.elm
|
|
||||||
|
|
||||||
elm-test Main.elm
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
elm-stuff
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
Copyright (c) 2016, Evan Czaplicki
|
|
||||||
|
|
||||||
All rights reserved.
|
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without
|
|
||||||
modification, are permitted provided that the following conditions are met:
|
|
||||||
|
|
||||||
* Redistributions of source code must retain the above copyright
|
|
||||||
notice, this list of conditions and the following disclaimer.
|
|
||||||
|
|
||||||
* Redistributions in binary form must reproduce the above
|
|
||||||
copyright notice, this list of conditions and the following
|
|
||||||
disclaimer in the documentation and/or other materials provided
|
|
||||||
with the distribution.
|
|
||||||
|
|
||||||
* Neither the name of Evan Czaplicki nor the names of other
|
|
||||||
contributors may be used to endorse or promote products derived
|
|
||||||
from this software without specific prior written permission.
|
|
||||||
|
|
||||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
||||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
|
||||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
|
||||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
||||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
|
||||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
||||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
||||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
||||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
||||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# Mess with the DOM
|
|
||||||
|
|
||||||
Sometimes you want to manually set the **focus** to a particular input field.
|
|
||||||
|
|
||||||
Other times you want to be able to control how things **scroll**.
|
|
||||||
|
|
||||||
This library makes it possible to do these kinds of operations as tasks.
|
|
||||||
Binary file not shown.
Binary file not shown.
|
Before Width: | Height: | Size: 54 KiB |
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"version": "1.1.1",
|
|
||||||
"summary": "DOM helpers for managing focus and scrolling.",
|
|
||||||
"repository": "http://github.com/elm-lang/dom.git",
|
|
||||||
"license": "BSD3",
|
|
||||||
"source-directories": [
|
|
||||||
"src"
|
|
||||||
],
|
|
||||||
"exposed-modules": [
|
|
||||||
"Dom",
|
|
||||||
"Dom.Scroll",
|
|
||||||
"Dom.LowLevel"
|
|
||||||
],
|
|
||||||
"native-modules": true,
|
|
||||||
"dependencies": {
|
|
||||||
"elm-lang/core": "4.0.0 <= v < 6.0.0"
|
|
||||||
},
|
|
||||||
"elm-version": "0.17.0 <= v < 0.19.0"
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
module Dom exposing
|
|
||||||
( focus, blur, Id
|
|
||||||
, Error(..)
|
|
||||||
)
|
|
||||||
|
|
||||||
{-|
|
|
||||||
|
|
||||||
# Focus
|
|
||||||
@docs focus, blur, Id
|
|
||||||
|
|
||||||
# Errors
|
|
||||||
@docs Error
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Native.Dom
|
|
||||||
import Task exposing (Task)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- ERRORS
|
|
||||||
|
|
||||||
|
|
||||||
{-| All the functions here look up DOM nodes by ID. If you ask for an ID that
|
|
||||||
is not currently attached to the DOM, you will get this error!
|
|
||||||
-}
|
|
||||||
type Error = NotFound String
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- FOCUS
|
|
||||||
|
|
||||||
|
|
||||||
{-| A unique identifier for a particular DOM node. When you create
|
|
||||||
`<div id="my-thing"></div>` you would refer to it with the `Id` `"my-thing"`.
|
|
||||||
-}
|
|
||||||
type alias Id =
|
|
||||||
String
|
|
||||||
|
|
||||||
|
|
||||||
{-| On a website, there can only be one thing in focus at a time. A text field,
|
|
||||||
a check box, etc. This function tells the Elm runtime to move the focus to a
|
|
||||||
particular DOM node.
|
|
||||||
|
|
||||||
Dom.focus "my-thing"
|
|
||||||
|
|
||||||
This is roughly the same as saying `document.getElementById(id).focus()`.
|
|
||||||
|
|
||||||
NOTE: setting focus can silently fail if the element is invisible. This could be captured as an error by checking to see
|
|
||||||
if document.activeElement actually got updated to the element we selected. https://jsbin.com/xeletez/edit?html,js,output
|
|
||||||
-}
|
|
||||||
focus : Id -> Task Error ()
|
|
||||||
focus =
|
|
||||||
Native.Dom.focus
|
|
||||||
|
|
||||||
|
|
||||||
{-| On a website, there can only be one thing in focus at a time. A text field,
|
|
||||||
a check box, etc. Sometimes you want that thing to no longer be in focus. This
|
|
||||||
is called “blur” for reasons that are unclear to almost everybody.
|
|
||||||
So this function tells a particular DOM node to lose focus.
|
|
||||||
|
|
||||||
Dom.blur "my-thing"
|
|
||||||
|
|
||||||
This is roughly the same as saying `document.getElementById(id).blur()`.
|
|
||||||
-}
|
|
||||||
blur : Id -> Task Error ()
|
|
||||||
blur =
|
|
||||||
Native.Dom.blur
|
|
||||||
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
module Dom.LowLevel exposing
|
|
||||||
( onDocument
|
|
||||||
, onWindow
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| This is not for general use. It backs libraries like `elm-lang/mouse` and
|
|
||||||
`elm-lang/window` which should cover your needs in most cases. In the rare
|
|
||||||
case that those packages do not seem to cover your scenario, first bring it up
|
|
||||||
with the community. Ask around and learn stuff first! Only get into these
|
|
||||||
functions after that.
|
|
||||||
|
|
||||||
# Global Event Listeners
|
|
||||||
@docs onDocument, onWindow
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Json.Decode as Json
|
|
||||||
import Native.Dom
|
|
||||||
import Task exposing (Task)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Add an event handler on the `document`. The resulting task will never end,
|
|
||||||
and when you kill the process it is on, it will detach the relevant JavaScript
|
|
||||||
event listener.
|
|
||||||
-}
|
|
||||||
onDocument : String -> Json.Decoder msg -> (msg -> Task Never ()) -> Task Never Never
|
|
||||||
onDocument =
|
|
||||||
Native.Dom.onDocument
|
|
||||||
|
|
||||||
|
|
||||||
{-| Add an event handler on `window`. The resulting task will never end, and
|
|
||||||
when you kill the process it is on, it will detach the relevant JavaScript
|
|
||||||
event listener.
|
|
||||||
-}
|
|
||||||
onWindow : String -> Json.Decoder msg -> (msg -> Task Never ()) -> Task Never Never
|
|
||||||
onWindow =
|
|
||||||
Native.Dom.onWindow
|
|
||||||
@@ -1,119 +0,0 @@
|
|||||||
module Dom.Scroll exposing
|
|
||||||
( toTop, toBottom, y, toY
|
|
||||||
, toLeft, toRight, x, toX
|
|
||||||
)
|
|
||||||
|
|
||||||
{-| When you set `overflow-y: scroll` on an element, a scroll bar will appear
|
|
||||||
when the content overflows the available space. When that happens, you may want
|
|
||||||
to modify the scroll position yourself. For example, maybe you have a chat room
|
|
||||||
that autoscrolls as new messages come in. This module provides functions like
|
|
||||||
`Dom.Scroll.toBottom` that let you do that kind of thing.
|
|
||||||
|
|
||||||
# Vertical
|
|
||||||
@docs toTop, toBottom, y, toY
|
|
||||||
|
|
||||||
# Horizontal
|
|
||||||
@docs toLeft, toRight, x, toX
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Dom exposing (Error, Id)
|
|
||||||
import Dom.Size as Size
|
|
||||||
import Native.Dom
|
|
||||||
import Task exposing (Task)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- VERTICAL
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the node with the given `Id` and scroll it to the top.
|
|
||||||
|
|
||||||
So `toTop id` is the same as `toY id 0`.
|
|
||||||
-}
|
|
||||||
toTop : Id -> Task Error ()
|
|
||||||
toTop id =
|
|
||||||
toY id 0
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the node with the given `Id` and scroll it to the bottom.
|
|
||||||
-}
|
|
||||||
toBottom : Id -> Task Error ()
|
|
||||||
toBottom =
|
|
||||||
Native.Dom.toBottom
|
|
||||||
|
|
||||||
|
|
||||||
{-| How much this element is scrolled vertically.
|
|
||||||
|
|
||||||
Say you have a node that does not fit in its container. A scroll bar shows up.
|
|
||||||
Initially you are at the top, which means `y` is `0`. If you scroll down 300
|
|
||||||
pixels, `y` will be `300`.
|
|
||||||
|
|
||||||
This is roughly the same as saying [`document.getElementById(id).scrollTop`][docs].
|
|
||||||
|
|
||||||
[docs]: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop
|
|
||||||
-}
|
|
||||||
y : Id -> Task Error Float
|
|
||||||
y =
|
|
||||||
Native.Dom.getScrollTop
|
|
||||||
|
|
||||||
|
|
||||||
{-| Set the vertical scroll to whatever offset you want.
|
|
||||||
|
|
||||||
Imagine you have a chat room and you want to control how it scrolls. Say the
|
|
||||||
full chat is 400 pixels long, but it is in a box that limits the visible height
|
|
||||||
to 100 pixels.
|
|
||||||
|
|
||||||
- If we say `toY "chat" 0` it will scroll to the very top.
|
|
||||||
- If we say `toY "chat" 300` it will be at the bottom.
|
|
||||||
|
|
||||||
If we provide values outside that range, they just get clamped, so
|
|
||||||
`toY "chat" 900` is also scrolled to the bottom.
|
|
||||||
-}
|
|
||||||
toY : Id -> Float -> Task Error ()
|
|
||||||
toY =
|
|
||||||
Native.Dom.setScrollTop
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
-- HORIZONTAL
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the node with the given `Id` and scroll it to the far left.
|
|
||||||
|
|
||||||
So `toLeft id` is the same as `toX id 0`.
|
|
||||||
-}
|
|
||||||
toLeft : Id -> Task Error ()
|
|
||||||
toLeft id =
|
|
||||||
toX id 0
|
|
||||||
|
|
||||||
|
|
||||||
{-| Find the node with the given `Id` and scroll it to the far right.
|
|
||||||
-}
|
|
||||||
toRight : Id -> Task Error ()
|
|
||||||
toRight =
|
|
||||||
Native.Dom.toRight
|
|
||||||
|
|
||||||
|
|
||||||
{-| How much this element is scrolled horizontally.
|
|
||||||
|
|
||||||
Say you have a node that does not fit in its container. A scroll bar shows up.
|
|
||||||
Initially you are at the far left, which means `x` is `0`. If you scroll right
|
|
||||||
300 pixels, `x` will be `300`.
|
|
||||||
|
|
||||||
This is roughly the same as saying [`document.getElementById(id).scrollLeft`][docs].
|
|
||||||
|
|
||||||
[docs]: https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft
|
|
||||||
-}
|
|
||||||
x : Id -> Task Error Float
|
|
||||||
x =
|
|
||||||
Native.Dom.getScrollLeft
|
|
||||||
|
|
||||||
|
|
||||||
{-| Set the horizontal scroll to whatever offset you want.
|
|
||||||
|
|
||||||
It works just like `toY`, so check out those docs for a more complete example.
|
|
||||||
-}
|
|
||||||
toX : Id -> Float -> Task Error ()
|
|
||||||
toX =
|
|
||||||
Native.Dom.setScrollLeft
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
module Dom.Size exposing ( height, width, Boundary )
|
|
||||||
|
|
||||||
{-| Figuring out the size of a node is actually pretty tricky. Nodes have
|
|
||||||
padding, borders, and margins. You can also scroll the content in a node in
|
|
||||||
many situations. Here it is as a picture:
|
|
||||||
|
|
||||||
<img src=""></img>
|
|
||||||
|
|
||||||
This module lets you choose one of these boundaries and then measure its size.
|
|
||||||
|
|
||||||
# Sizes
|
|
||||||
@docs width, height, Boundary
|
|
||||||
|
|
||||||
-}
|
|
||||||
|
|
||||||
import Dom exposing (Error, Id)
|
|
||||||
import Native.Dom
|
|
||||||
import Task exposing (Task)
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the height of a node, measured along a certain boundary.
|
|
||||||
|
|
||||||
If the node has the `hidden` attribute or the `display: none` style, this
|
|
||||||
will be zero.
|
|
||||||
-}
|
|
||||||
height : Boundary -> Id -> Task Error Float
|
|
||||||
height =
|
|
||||||
Native.Dom.height
|
|
||||||
|
|
||||||
|
|
||||||
{-| Get the width of a node, measured along a certain boundary.
|
|
||||||
|
|
||||||
If the node has the `hidden` attribute or the `display: none` style, this
|
|
||||||
will be zero.
|
|
||||||
-}
|
|
||||||
width : Boundary -> Id -> Task Error Float
|
|
||||||
width =
|
|
||||||
Native.Dom.width
|
|
||||||
|
|
||||||
|
|
||||||
{-| Check out [this diagram][diagram] to understand what all of these
|
|
||||||
boundaries refer to.
|
|
||||||
|
|
||||||
If you happen to know the JavaScript equivalents by heart, maybe this will help you:
|
|
||||||
|
|
||||||
- `scrollHeight` is the same as `height Content`
|
|
||||||
- `clientHeight` is the same as `height VisibleContent`
|
|
||||||
- `offsetHeight` is the same as `height VisibleContentWithBorders`
|
|
||||||
- `getBoundingClientRect().height` is the same as `height VisibleContentWithBordersAndMargins`
|
|
||||||
|
|
||||||
But again, look at [the diagram][diagram]. It makes this easier!
|
|
||||||
|
|
||||||
[diagram]:
|
|
||||||
-}
|
|
||||||
type Boundary
|
|
||||||
= Content
|
|
||||||
| VisibleContent
|
|
||||||
| VisibleContentWithBorders
|
|
||||||
| VisibleContentWithBordersAndMargins
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user