on master help sign in
Merge branch 'master' into stable
commit806097075e12d95e91389060bb5e263a51f9a643 authorEvan Czaplicki <info@elm-lang.org> dateWed 30 Apr 2014, 02:55 (13 years ago) parent4e88d397862d20
80 files changed+1653−846
.gitignore+4 .gitmodules+9 CONTRIBUTING.md+20−19 Elm-Test+1 Elm.cabal+26−16 IO+1 README.md+12−47 automaton+1 changelog.md+15 compiler/Elm/Internal/Dependencies.hs+41−4 compiler/Elm/Internal/Name.hs+2−2 compiler/Generate/JavaScript.hs+1−2 compiler/Generate/JavaScript/Ports.hs+15−16 compiler/Metadata/Prelude.hs+3−1 compiler/Parse/Helpers.hs+5−3 compiler/Type/ExtraChecks.hs+10−14 compiler/Type/Unify.hs+5−1 libraries/Array.elm+189 libraries/Dict.elm+76−18 libraries/Graphics/Collage.elm−1 libraries/Graphics/Element.elm+10−13 libraries/Graphics/Input.elm+1−1 libraries/JavaScript.elm−74 libraries/JavaScript/Experimental.elm+31−16 libraries/Json.elm+25−110 libraries/List.elm+25−15 libraries/Native/Array.js+597 libraries/Native/Date.js+1−2 libraries/Native/Graphics/Input.js+9−38 libraries/Native/JavaScript.js+59−91 libraries/Native/Json.js+71−58 libraries/Native/Regex.js+14−14 libraries/Native/Show.js+4 libraries/Native/Signal/Http.js+6−7 libraries/Native/Signal/Touch.js+3−3 libraries/Native/Signal/WebSocket.js+3−5 libraries/Native/String.js+9−9 libraries/Native/Text.js+1 libraries/Native/Utils.js+39−22 libraries/Set.elm+11−1 libraries/Signal.elm+8−13 libraries/String.elm+1−1 libraries/Text.elm−1 libraries/elm_dependencies.json+6−6 runtime/Render/Element.js+8−2 server/LICENSE−30 server/Server.hs−127 server/Setup.hs−2 server/elm-server.cabal−41 tests/elm/Test.elm+37 tests/elm/Test/Array.elm+54 tests/elm/Test/Dict.elm+26 tests/elm/Test/List.elm+38 tests/elm/Test/Set.elm+27 tests/elm/Test/String.elm+34 tests/elm/Test/Trampoline.elm+29 tests/Main.hs → tests/hs/CompilerTest.hs tests/hs/LibTest.hs+30 tests/Tests/Compiler.hs → tests/hs/Tests/Compiler.hs tests/Tests/Property.hs → tests/hs/Tests/Property.hs tests/Tests/Property/Arbitrary.hs → tests/hs/Tests/Property/Arbitrary.hs tests/test-files/bad/BBTArgs → tests/hs/test-files/bad/BBTArgs tests/test-files/bad/InfiniteType.elm → tests/hs/test-files/bad/InfiniteType.elm tests/test-files/bad/NonElementMain.elm → tests/hs/test-files/bad/NonElementMain.elm tests/test-files/bad/Strings/ExtraClose.elm → tests/hs/test-files/bad/Strings/ExtraClose.elm tests/test-files/good/AliasSubstitution.elm → tests/hs/test-files/good/AliasSubstitution.elm tests/test-files/good/NoExpressions.elm → tests/hs/test-files/good/NoExpressions.elm tests/test-files/good/Otherwise.elm → tests/hs/test-files/good/Otherwise.elm tests/test-files/good/Ports.elm → tests/hs/test-files/good/Ports.elm tests/test-files/good/QuotesAndComments.elm → tests/hs/test-files/good/QuotesAndComments.elm tests/test-files/good/Soundness/Apply.elm → tests/hs/test-files/good/Soundness/Apply.elm tests/test-files/good/Soundness/ApplyAnnotated.elm → tests/hs/test-files/good/Soundness/ApplyAnnotated.elm tests/test-files/good/Soundness/Id.elm → tests/hs/test-files/good/Soundness/Id.elm tests/test-files/good/Soundness/IdAnnotated.elm → tests/hs/test-files/good/Soundness/IdAnnotated.elm tests/test-files/good/Soundness/TrickyId.elm → tests/hs/test-files/good/Soundness/TrickyId.elm tests/test-files/good/Soundness/TrickyIdAnnotated.elm → tests/hs/test-files/good/Soundness/TrickyIdAnnotated.elm tests/test-files/good/Strings/Multiline.elm → tests/hs/test-files/good/Strings/Multiline.elm tests/test-files/good/Strings/MultilineNormal.elm → tests/hs/test-files/good/Strings/MultilineNormal.elm tests/test-files/good/Unify/LockedVars.elm → tests/hs/test-files/good/Unify/LockedVars.elm tests/test-files/good/Unify/NonHomogeneousRecords.elm → tests/hs/test-files/good/Unify/NonHomogeneousRecords.elm
.gitignore+4
@@ -11,3 +11,7 @@ data
1111*/ElmFiles/*
1212.DS_Store
1313*~
14+tests/elm/build/
15+tests/elm/cache/
16+tests/elm/exe.js
17+node_modules/
.gitmodulesnew file+9
@@ -0,0 +1,9 @@
1+[submodule "IO"]
2+ path = IO
3+ url = https://github.com/maxsnew/IO.git
4+[submodule "automaton"]
5+ path = automaton
6+ url = https://github.com/evancz/automaton.git
7+[submodule "Elm-Test"]
8+ path = Elm-Test
9+ url = https://github.com/deadfoxygrandpa/Elm-Test.git
CONTRIBUTING.md+20−19
@@ -5,31 +5,32 @@ describes the basic standards for opening pull requests:
55
66## Branches
77
8− * The master branch of Elm is for tagging previous releases
9− and non-breaking changes, like bug fixes. This branch is
10− handy for folks who want to build the most recent public
11− release from source.
8+ * [The master branch](https://github.com/elm-lang/Elm/tree/master) is the
9+ home of the next release of the compiler so new features and improvements
10+ get merged there. Most pull requests should target this branch!
1211
13− * The dev branch is where the most active development occurs.
14− It is the home of the next release of the compiler so new
15− features and improvements get merged there.
12+ * [The stable branch](https://github.com/elm-lang/Elm/tree/stable) is for
13+ tagging previous releases and critical bug fixes. This branch is handy for
14+ folks who want to build the most recent public release from source.
15+
16+If you are working on a fairly large feature, we will probably want to merge it
17+in as its own branch and do some testing before bringing it into the master
18+branch. This way we can keep releases of the master branch independent of new
19+features.
1620
1721## Opening a pull request
1822
19−**Please open PRs against the [dev branch of
20−Elm](http://github.com/evancz/elm/tree/dev) whenever possible.** Changes that
21−are compatible with the API of master will be merged to master as soon as
22−possible. Changes that affect Elm's API will be merged to master with a future
23−release of the compiler.
23+**Please open PRs against the [master branch of
24+Elm](http://github.com/elm-lang/elm/tree/master) whenever possible.**
2425
25−Note that changes in the master branch of [Elm](https://github.com/evancz/Elm/)
26+Note that changes in the master branch of [Elm](https://github.com/elm-lang/Elm/)
2627are able to be compiled against the master branch of the [elm-lang.org
27−repo](https://github.com/evancz/elm-lang.org), and the dev branch of
28−[Elm](https://github.com/evancz/Elm/) is able to be built with the dev branch of
29−the [elm-lang.org repo](https://github.com/evancz/elm-lang.org). Please make
30−sure that your changes maintain this compatibility.
28+repo](https://github.com/elm-lang/elm-lang.org), and the stable branch of
29+[Elm](https://github.com/elm-lang/Elm/) is able to be built with the stable
30+branch of the [elm-lang.org repo](https://github.com/elm-lang/elm-lang.org).
31+Please make sure that your changes maintain this compatibility.
3132
3233## Licensing
3334
34−You should sign the [contributor agreement](ContributorAgreement.pdf)
35−and send it to <info@elm-lang.org> before opening your pull request.
35+You need to sign the [contributor agreement](ContributorAgreement.pdf)
36+and send it to <info@elm-lang.org> before opening your pull request.
Elm-Testnew file+1

Submodule changed.

Elm.cabal+26−16
@@ -1,5 +1,5 @@
11Name: Elm
2−Version: 0.12
2+Version: 0.12.1
33Synopsis: The Elm language module.
44Description: Elm aims to make client-side web-development more pleasant.
55 It is a statically/strongly typed, functional reactive
@@ -95,8 +95,9 @@ Library
9595 Paths_Elm
9696
9797 Build-depends: aeson,
98+ aeson-pretty,
9899 base >=4.2 && <5,
99− binary >= 0.6.4.0,
100+ binary >= 0.7.0.0,
100101 blaze-html >= 0.5 && < 0.8,
101102 blaze-markup,
102103 bytestring,
@@ -177,8 +178,9 @@ Executable elm
177178 Paths_Elm
178179
179180 Build-depends: aeson,
181+ aeson-pretty,
180182 base >=4.2 && <5,
181− binary >= 0.6.4.0,
183+ binary >= 0.7.0.0,
182184 blaze-html >= 0.5 && < 0.8,
183185 blaze-markup,
184186 bytestring,
@@ -222,7 +224,7 @@ Executable elm-doc
222224 Build-depends: aeson,
223225 aeson-pretty,
224226 base >=4.2 && <5,
225− binary >= 0.6.4.0,
227+ binary >= 0.7.0.0,
226228 bytestring,
227229 cmdargs,
228230 containers >= 0.3,
@@ -236,35 +238,33 @@ Executable elm-doc
236238 text,
237239 union-find
238240
239−Test-Suite test-elm
241+Test-Suite compiler-tests
240242 Type: exitcode-stdio-1.0
241− Hs-Source-Dirs: tests, compiler
242− Main-is: Main.hs
243+ Hs-Source-Dirs: tests/hs, compiler
244+ Main-is: CompilerTest.hs
243245 other-modules: Tests.Compiler
244246 Tests.Property
245247 Tests.Property.Arbitrary
246248 SourceSyntax.Helpers
247249 SourceSyntax.Literal
248250 SourceSyntax.PrettyPrint
249− build-depends: base,
250− directory,
251− Elm,
252− test-framework,
251+ build-depends: test-framework,
253252 test-framework-hunit,
254253 test-framework-quickcheck2 >= 0.3,
255254 HUnit,
256− pretty,
257255 QuickCheck >= 2 && < 3,
258− filemanip,
259256 aeson,
257+ aeson-pretty,
260258 base >=4.2 && <5,
261− binary >= 0.6.4.0,
262− blaze-html == 0.5.* || == 0.6.*,
263− blaze-markup == 0.5.1.*,
259+ binary >= 0.7.0.0,
260+ blaze-html,
261+ blaze-markup,
264262 bytestring,
265263 cmdargs,
266264 containers >= 0.3,
267265 directory,
266+ Elm,
267+ filemanip,
268268 filepath,
269269 indents,
270270 language-ecmascript >=0.15 && < 1.0,
@@ -276,3 +276,13 @@ Test-Suite test-elm
276276 transformers >= 0.2,
277277 union-find,
278278 unordered-containers
279+
280+Test-Suite lib-tests
281+ Type: exitcode-stdio-1.0
282+ Hs-Source-Dirs: tests/hs
283+ Main-is: LibTest.hs
284+ build-depends: base,
285+ directory,
286+ Elm,
287+ filepath,
288+ process
IOnew file+1

Submodule changed.

README.md+12−47
@@ -1,58 +1,23 @@
11Learn about the Elm programming language at [elm-lang.org](http://elm-lang.org/).
22
3−[![Build Status](https://travis-ci.org/evancz/Elm.png)](https://travis-ci.org/evancz/Elm)
3+[![Build Status](https://travis-ci.org/elm-lang/Elm.png)](https://travis-ci.org/elm-lang/Elm)
44
55## Install
66
7−**Arch Linux** &mdash; follow [these directions](https://github.com/evancz/Elm/wiki/Installing-Elm#arch-linux) and then
8−jump to the [My First Project](#my-first-project) section.
9−<br/>
10−**OS X 10.9 or XCode 5** &mdash; follow
11−[these directions](http://justtesting.org/post/64947952690/the-glasgow-haskell-compiler-ghc-on-os-x-10-9)
12−before continuing with the platform agnostic directions below.
7+Follow [these instructions][installer] if you just want to use Elm. To build
8+the compiler from source, run the following commands:
139
14−**Platform Agnostic** &mdash;
15−download the [Haskell Platform 2012.2.0.0 or later](http://hackage.haskell.org/platform/).
16−Once the Haskell Platform is installed:
10+ [installer]: https://github.com/elm-lang/elm-platform/blob/master/README.md#elm-platform
1711
18− cabal update
19− cabal install elm
20− cabal install elm-server
12+```bash
13+git clone https://github.com/elm-lang/Elm.git
14+cd Elm
15+cabal configure
16+cabal build
17+```
2118
22−## Use
23−
24−To use `elm` and `elm-server` you may need to add a new directory to your PATH.
25−
26−Cabal should tell you where your executables are located upon
27−successful installation. It'll be something like `/home/evan/.cabal/bin`
28−which you should append to your PATH variable.
29−See this tutorial if you are new to changing your PATH in
30−[Unix/Linux](http://www.cyberciti.biz/faq/unix-linux-adding-path/).
31−
32−## My First Project
33−
34−Now we will create a simple Elm project.
35−The following commands will set-up a very basic project and start the Elm server.
36−
37− mkdir helloElm
38− cd helloElm
39− printf "import Mouse\n\nmain = lift asText Mouse.position" > Main.elm
40− elm-server
41−
42−The first two commands create a new directory and navigate into it. The `printf`
43−commands place a simple program into `Main.elm`. Do this manually if you do not
44−have `printf`. The final command starts the Elm server at [localhost:8000](http://localhost:8000/),
45−allowing you to navigate to `Main.elm` and see your first program in action.
46−
47−#### Final Notes
48−
49−The `elm` package provides
50−[some utility functions](http://hackage.haskell.org/package/Elm) for
51−working with Elm in Haskell. This can be useful for creating tooling
52−for Elm, and has been useful for projects like
53−[the website](http://elm-lang.org/) and
54−[`elm-get`](https://github.com/evancz/elm-get). Email the list if you
55−want to rely on these functions!
19+This will build the compiler in `/dist/build/elm/elm` but it will not be on
20+your PATH.
5621
5722If you are stuck, email
5823[the list](https://groups.google.com/forum/?fromgroups#!forum/elm-discuss)
automatonnew file+1

Submodule changed.

changelog.md+15
@@ -1,4 +1,19 @@
11
2+## 0.12.1
3+
4+## Improvements:
5+
6+ * New Array library (thanks entirely to @Xashili)
7+ * Json.Value can flow through ports
8+ * Improve speed and stack usage in List library (thanks to @maxsnew)
9+ * Add Dict.filter and Dict.partition (thanks to @hdgarrood)
10+
11+#### Breaking Changes:
12+
13+ * Revamp Json library, simpler with better names
14+ * Revamp JavaScript.Experimental library to have slightly better names
15+ * Remove JavaScript library which was made redundant by ports
16+
217## 0.12
318
419#### Breaking Changes:
compiler/Elm/Internal/Dependencies.hs+41−4
@@ -5,9 +5,12 @@ import Control.Applicative
55import Control.Monad.Error
66import qualified Control.Exception as E
77import Data.Aeson
8+import Data.Aeson.Encode.Pretty
89import qualified Data.List as List
910import qualified Data.ByteString.Lazy as BS
1011import qualified Data.HashMap.Strict as Map
12+import qualified Data.Text as T
13+
1114import qualified Elm.Internal.Name as N
1215import qualified Elm.Internal.Version as V
1316import qualified Elm.Internal.Paths as Path
@@ -22,14 +25,32 @@ data Deps = Deps
2225 , exposed :: [String]
2326 , elmVersion :: V.Version
2427 , dependencies :: [(N.Name,V.Version)]
25− } deriving Show
28+ } deriving (Show, Eq, Ord)
2629
2730data MiniDeps = Mini [(N.Name,V.Version)]
31+ deriving (Show, Eq, Ord)
32+
33+instance ToJSON MiniDeps where
34+ toJSON (Mini m) = toJSON m
2835
2936instance FromJSON MiniDeps where
3037 parseJSON (Object obj) = Mini <$> getDependencies obj
3138 parseJSON _ = mzero
3239
40+instance ToJSON Deps where
41+ toJSON d = object [
42+ "version" .= version d,
43+ "summary" .= summary d,
44+ "description" .= description d,
45+ "license" .= license d,
46+ "repository" .= repo d,
47+ "exposed-modules" .= exposed d,
48+ "elm-version" .= elmVersion d,
49+ "dependencies" .= (jsonDeps . dependencies $ d)
50+ ]
51+ where jsonDeps = Map.fromList . map (mapFst (T.pack . show))
52+ mapFst f (a,b) = (f a, b)
53+
3354instance FromJSON Deps where
3455 parseJSON (Object obj) =
3556 do version <- get obj "version" "your projects version number"
@@ -110,8 +131,24 @@ withDeps handle path =
110131 case result of
111132 Right bytes -> return bytes
112133 Left _ -> throwError $
113− "could not find file " ++ path ++
114− "\n You may need to create a dependency file for your project."
134+ "could not find " ++ path ++ " file. You may need to create one.\n" ++
135+ " For an example of how to fill in the dependencies file, check out\n" ++
136+ " <https://github.com/evancz/automaton/blob/master/elm_dependencies.json>"
115137
116138depsAt :: FilePath -> ErrorT String IO Deps
117−depsAt = withDeps id
139+depsAt = withDeps id
140+
141+-- | Encode dependencies in a canonical JSON format
142+prettyJSON :: Deps -> BS.ByteString
143+prettyJSON = encodePretty' config
144+ where config = defConfig { confCompare = order }
145+ order = keyOrder [ "name",
146+ "version",
147+ "summary",
148+ "description",
149+ "license",
150+ "repo",
151+ "exposed-modules",
152+ "elm-version",
153+ "dependencies"
154+ ]
compiler/Elm/Internal/Name.hs+2−2
@@ -10,7 +10,7 @@ import qualified Data.Maybe as Maybe
1010import Data.Typeable
1111
1212data Name = Name { user :: String, project :: String }
13− deriving (Typeable,Eq)
13+ deriving (Typeable,Eq, Ord)
1414
1515instance Binary Name where
1616 get = Name <$> get <*> get
@@ -48,4 +48,4 @@ errorMsg string =
4848 unlines
4949 [ "Dependency file has an invalid name: " ++ string
5050 , "Must have format user/project and match a public github project."
51− ]
51+ ]
compiler/Generate/JavaScript.hs+1−2
@@ -39,7 +39,6 @@ internalImports name =
3939 , include "_U" "_N.Utils"
4040 , include "_L" "_N.List"
4141 , include "_E" "_N.Error"
42− , include "_J" "_N.JavaScript"
4342 , varDecl "$moduleName" (string name)
4443 ]
4544
@@ -164,7 +163,7 @@ expression (A region expr) =
164163
165164 ExplicitList es ->
166165 do es' <- mapM expression es
167− return $ obj "_J.toList" <| ArrayLit () es'
166+ return $ obj "_L.fromArray" <| ArrayLit () es'
168167
169168 Data name es ->
170169 do es' <- mapM expression es
compiler/Generate/JavaScript/Ports.hs+15−16
@@ -20,7 +20,7 @@ check x jsType continue =
2020 JSNumber -> [typeof "number"]
2121 JSBoolean -> [typeof "boolean"]
2222 JSString -> [typeof "string", instanceof "String"]
23− JSArray -> [instanceof "Array"]
23+ JSArray -> [(obj "_U.isJSArray" <|)]
2424 JSObject fields -> [jsFold OpLAnd (typeof "object" : map member fields)]
2525
2626incoming :: Type -> Expression ()
@@ -35,17 +35,17 @@ inc tipe x =
3535 case tipe of
3636 Lambda _ _ -> error "functions should not be allowed through input ports"
3737 Var _ -> error "type variables should not be allowed through input ports"
38+
39+ Data "Json.Value" [] ->
40+ obj "Native.Json.fromJS" <| x
41+
3842 Data ctor []
39− | ctor == "Int" -> elm JSNumber
40− | ctor == "Float" -> elm JSNumber
41− | ctor == "Bool" -> elm JSBoolean
42− | ctor == "String" -> elm JSString
43− | ctor == "JavaScript.JSNumber" -> js JSNumber
44− | ctor == "JavaScript.JSBool" -> js JSBoolean
45− | ctor == "JavaScript.JSString" -> js JSString
43+ | ctor == "Int" -> from JSNumber
44+ | ctor == "Float" -> from JSNumber
45+ | ctor == "Bool" -> from JSBoolean
46+ | ctor == "String" -> from JSString
4647 where
47− elm checks = check x checks (obj ("_J.to" ++ ctor) <| x)
48− js checks = check x checks x
48+ from checks = check x checks x
4949
5050 Data ctor [t]
5151 | ctor == "Maybe.Maybe" ->
@@ -54,7 +54,7 @@ inc tipe x =
5454 (obj "Maybe.Just" <| inc t x)
5555
5656 | ctor == "_List" ->
57− check x JSArray (obj "_J.toList" <| array)
57+ check x JSArray (obj "_L.fromArray" <| array)
5858 where
5959 array = DotRef () x (var "map") <| incoming t
6060
@@ -104,10 +104,9 @@ out tipe x =
104104
105105 Var _ -> error "type variables should not be allowed through input ports"
106106 Data ctor []
107− | ctor `elem` ["Int","Float","Bool","String"] -> obj ("_J.from" ++ ctor) <| x
108− | ctor `elem` jsPrims -> x
109− where
110− jsPrims = map ("JavaScript.JS"++) ["Number","Bool","String"]
107+ | ctor `elem` ["Int","Float","Bool","String"] -> x
108+ | ctor == "Json.Value" ->
109+ obj "Native.Json.toJS" <| x
111110
112111 Data ctor [t]
113112 | ctor == "Maybe.Maybe" ->
@@ -116,7 +115,7 @@ out tipe x =
116115 (out t (DotRef () x (var "_0")))
117116
118117 | ctor == "_List" ->
119− DotRef () (obj "_J.fromList" <| x) (var "map") <| outgoing t
118+ DotRef () (obj "_L.toArray" <| x) (var "map") <| outgoing t
120119
121120 Data ctor ts
122121 | Help.isTuple ctor ->
compiler/Metadata/Prelude.hs+3−1
@@ -25,7 +25,9 @@ prelude = string ++ text ++ map (\n -> (n, Hiding [])) modules
2525 text = map ((,) "Text") [ As "Text", Hiding ["link", "color", "height"] ]
2626 string = map ((,) "String") [ As "String", Importing ["show"] ]
2727 modules = [ "Basics", "Signal", "List", "Maybe", "Time", "Color"
28− , "Graphics.Element", "Graphics.Collage", "Native.Ports" ]
28+ , "Graphics.Element", "Graphics.Collage"
29+ , "Native.Ports", "Native.Json"
30+ ]
2931
3032interfaces :: Bool -> IO Interfaces
3133interfaces noPrelude =
compiler/Parse/Helpers.hs+5−3
@@ -79,9 +79,11 @@ innerVarChar :: IParser Char
7979innerVarChar = alphaNum <|> char '_' <|> char '\'' <?> ""
8080
8181makeVar :: IParser Char -> IParser String
82−makeVar p = do v <- (:) <$> p <*> many innerVarChar
83− guard (v `notElem` reserveds)
84− return v
82+makeVar p = do
83+ v <- (:) <$> p <*> many innerVarChar
84+ if v `elem` reserveds
85+ then fail $ "unexpected keyword '" ++ v ++ "', variables cannot be keywords"
86+ else return v
8587
8688reserved :: String -> IParser String
8789reserved word =
compiler/Type/ExtraChecks.hs+10−14
@@ -8,7 +8,6 @@ module Type.ExtraChecks (mainType, occurs, portTypes) where
88
99import Control.Applicative ((<$>),(<*>))
1010import Control.Monad.State
11−import qualified Data.List as List
1211import qualified Data.Map as Map
1312import qualified Data.Traversable as Traverse
1413import qualified Data.UnionFind.IO as UF
@@ -56,9 +55,15 @@ portTypes rules expr =
5655 isValid isTopLevel seenFunc seenSignal direction name tipe =
5756 case tipe of
5857 ST.Data ctor ts
59− | isJs ctor || isElm ctor -> mapM_ valid ts
58+ | validConstructor -> mapM_ valid ts
6059 | ctor == "Signal.Signal" -> handleSignal ts
6160 | otherwise -> err' True "an unsupported type"
61+ where
62+ primitives =
63+ ["Int","Float","String","Bool","Maybe.Maybe","_List","Json.Value"]
64+
65+ validConstructor =
66+ ctor `elem` primitives || Help.isTuple ctor
6267
6368 ST.Var _ -> err "free type variables"
6469
@@ -80,14 +85,6 @@ portTypes rules expr =
8085 isValid' = isValid False
8186 valid = isValid' seenFunc seenSignal direction name
8287
83− isJs ctor =
84− List.isPrefixOf "JavaScript." ctor
85− && length (filter (=='.') ctor) == 1
86−
87− isElm ctor =
88− ctor `elem` ["Int","Float","String","Bool","Maybe.Maybe","_List"]
89− || Help.isTuple ctor
90−
9188 handleSignal ts
9289 | seenFunc = err "functions that involve signals"
9390 | seenSignal = err "signals-of-signals"
@@ -104,10 +101,9 @@ portTypes rules expr =
104101 , " through port '", name, "' is invalid." ]
105102 , txt [ "It contains ", kind, ":\n" ]
106103 , (P.nest 4 . SPP.pretty $ Alias.realias rules tipe) <> P.text "\n"
107− , txt [ "Acceptable values for ", dir "incoming" "outgoing"
108− , " ports include JavaScript values and" ]
109− , txt [ "the following Elm values: Ints, Floats, Bools, Strings, Maybes," ]
110− , txt [ "Lists, Tuples, ", dir "" "first-order functions, ", "and concrete records." ]
104+ , txt [ "Acceptable values for ", dir "incoming" "outgoing", " ports include:" ]
105+ , txt [ " Ints, Floats, Bools, Strings, Maybes, Lists, Tuples," ]
106+ , txt [ " Json.Values, ", dir "" "first-order functions, ", "and concrete records." ]
111107 ] ++ if couldBeAlias then aliasWarning else []
112108
113109 aliasWarning =
compiler/Type/Unify.hs+5−1
@@ -86,6 +86,10 @@ actuallyUnify region variable1 variable2 = do
8686 TS.addError region (Just $ Maybe.fromMaybe msg maybe) variable1 variable2
8787 where msg = "A comparable must be an Int, Float, Char, String, list, or tuple."
8888
89+ appendableError maybe =
90+ TS.addError region (Just $ Maybe.fromMaybe msg maybe) variable1 variable2
91+ where msg = "An appendable must be of type String, List, or Text."
92+
8993 unifyComparable var name
9094 | name `elem` ["Int","Float","Char","String","comparable"] = flexAndUnify var
9195 | otherwise = comparableError Nothing
@@ -108,7 +112,7 @@ actuallyUnify region variable1 variable2 = do
108112 do struct <- liftIO $ collectApps varFlex
109113 case struct of
110114 List _ -> flexAndUnify varSuper
111− _ -> comparableError Nothing
115+ _ -> appendableError Nothing
112116
113117 rigidError variable = TS.addError region (Just hint) variable1 variable2
114118 where
libraries/Array.elmnew file+189
@@ -0,0 +1,189 @@
1+module Array where
2+
3+{-| A library for fast immutable arrays. The elements in an array must have the
4+same type. The arrays are implemented in Relaxed Radix Balanced-Trees for fast
5+reads, updates, and appends.
6+
7+# Creating Arrays
8+@docs empty, repeat, initialize, fromList
9+
10+# Basics
11+@docs length, push, append
12+
13+# Get and Set
14+@docs get, getOrElse, getOrFail, set
15+
16+# Taking Arrays Apart
17+@docs toList, toIndexedList, slice
18+
19+# Mapping and Folding
20+@docs map, indexedMap, foldl, foldr
21+-}
22+
23+import Native.Array
24+import Basics (..)
25+import Maybe (..)
26+import List
27+
28+data Array a = Array
29+
30+{-| Initialize an array. `initialize n f` creates an array of length `n` with
31+the element at index `i` initialized to the result of `(f i)`.
32+
33+ initialize 4 id == fromList [0,1,2,3]
34+ initialize 4 (\n -> n*n) == fromList [0,1,4,9]
35+ initialize 4 (always 0) == fromList [0,0,0,0]
36+-}
37+initialize : Int -> (Int -> a) -> Array a
38+initialize = Native.Array.initialize
39+
40+{-| Creates an array with a given length, filled with a default element.
41+
42+ repeat 5 0 == fromList [0,0,0,0,0]
43+ repeat 3 "cat" == fromList ["cat","cat","cat"]
44+
45+Notice that `repeat 3 x` is the same as `initialize 3 (always x)`.
46+-}
47+repeat : Int -> a -> Array a
48+repeat n e = initialize n (always e)
49+
50+{-| Create an array from a list. -}
51+fromList : [a] -> Array a
52+fromList = Native.Array.fromList
53+
54+{-| Create a list of elements from an array.
55+
56+ toList (fromList [3,5,8]) == [3,5,8]
57+-}
58+toList : Array a -> [a]
59+toList = Native.Array.toList
60+
61+-- TODO: make this a native function.
62+{-| Create an indexed list from an array. Each element of the array will be
63+paired with its index.
64+
65+ toIndexedList (fromList ["cat","dog"]) == [(0,"cat"), (1,"dog")]
66+-}
67+toIndexedList : Array a -> [(Int, a)]
68+toIndexedList array =
69+ List.zip [ 0 .. Native.Array.length array - 1 ] (Native.Array.toList array)
70+
71+{-| Apply a function on every element in an array.
72+
73+ map sqrt (fromList [1,4,9]) == fromList [1,2,3]
74+-}
75+map : (a -> b) -> Array a -> Array b
76+map = Native.Array.map
77+
78+{-| Apply a function on every element with its index as first argument.
79+
80+ indexedMap (*) (fromList [5,5,5]) == fromList [0,5,10]
81+-}
82+indexedMap : (Int -> a -> b) -> Array a -> Array b
83+indexedMap = Native.Array.indexedMap
84+
85+{-| Reduce an array from the left. Read `foldl` as &ldquo;fold from the left&rdquo;.
86+
87+ foldl (::) [] (fromList [1,2,3]) == [3,2,1]
88+-}
89+foldl : (a -> b -> b) -> b -> Array a -> b
90+foldl = Native.Array.foldl
91+
92+{-| Reduce an array from the right. Read `foldr` as &ldquo;fold from the right&rdquo;.
93+
94+ foldr (+) 0 (repeat 3 5) == 15
95+-}
96+foldr : (a -> b -> b) -> b -> Array a -> b
97+foldr = Native.Array.foldr
98+
99+{-| Keep only elements that satisfy the predicate:
100+
101+ filter isEven (fromList [1..6]) == (fromList [2,4,6])
102+-}
103+filter : (a -> Bool) -> Array a -> Array a
104+filter isOkay arr =
105+ let update x xs = if isOkay x then Native.Array.push x xs else xs
106+ in
107+ Native.Array.foldl update Native.Array.empty arr
108+
109+{-| Return an empty array.
110+
111+ length empty == 0
112+-}
113+empty : Array a
114+empty = Native.Array.empty
115+
116+{-| Push an element to the end of an array.
117+
118+ push 3 (fromList [1,2]) == fromList [1,2,3]
119+-}
120+push : a -> Array a -> Array a
121+push = Native.Array.push
122+
123+{-| Get the element at a particular index.
124+
125+ getOrFail 2 (A.fromList [3,2,1]) == 1
126+
127+Warning: this function will result in a runtime error if the index is not found,
128+so it is best to use `get` or `getOrElse` unless you are sure the index will be
129+found.
130+-}
131+getOrFail : Int -> Array a -> a
132+getOrFail = Native.Array.get
133+
134+{-| Return Just the element at the index or Nothing if the index is out of range.
135+
136+ get 2 (fromList [3,2,1]) == Just 2
137+ get 5 (fromList [3,2,1]) == Nothing
138+ get -1 (fromList [3,2,1]) == Nothing
139+-}
140+get : Int -> Array a -> Maybe a
141+get i array =
142+ if 0 <= i && i < Native.Array.length array
143+ then Just (Native.Array.get i array)
144+ else Nothing
145+
146+{-| Get the element at the index. Or if the index is out of range, a default
147+value is returned.
148+
149+ getOrElse 0 2 (fromList [3,2,1]) == 1
150+ getOrElse 0 5 (fromList [3,2,1]) == 0
151+-}
152+getOrElse : a -> Int -> Array a -> a
153+getOrElse default i array =
154+ if 0 <= i && i < Native.Array.length array
155+ then Native.Array.get i array
156+ else default
157+
158+{-| Set the element at a particular index. Returns an updated array.
159+If the index is out of range, the array is unaltered.
160+
161+ set 1 7 (fromList [1,2,3]) == fromList [1,7,3]
162+-}
163+set : Int -> a -> Array a -> Array a
164+set = Native.Array.set
165+
166+{-| Slice an array given a range. The selection is inclusive, so the last
167+element in the selection will also be in the new array. This may change in the
168+future. You can select from the end by giving a negative Int.
169+
170+ slice 1 2 (fromList [0,1,2,3,4]) == fromList [1,2]
171+ slice 1 -2 (fromList [0,1,2,3,4]) == fromList [1,2,3]
172+ slice -3 -2 (fromList [0,1,2,3,4]) == fromList [2,3]
173+-}
174+slice : Int -> Int -> Array a -> Array a
175+slice = Native.Array.slice
176+
177+{-| Return the length of an array.
178+
179+ length (fromList [1,2,3]) == 3
180+-}
181+length : Array a -> Int
182+length = Native.Array.length
183+
184+{-| Append two arrays to a new one.
185+
186+ append (array 2 42) (array 3 81) == fromList [42,42,81,81,81]
187+-}
188+append : Array a -> Array a -> Array a
189+append = Native.Array.append
libraries/Dict.elm+76−18
@@ -1,6 +1,8 @@
11module Dict (empty,singleton,insert,update
2− ,lookup,findWithDefault
2+ ,get,getOrElse,getOrFail
33 ,remove,member
4+ ,filter
5+ ,partition
46 ,foldl,foldr,map
57 ,union,intersect,diff
68 ,keys,values
@@ -17,7 +19,7 @@ Insert, remove, and query operations all take *O(log n)* time.
1719@docs empty, singleton, insert, update, remove
1820
1921# Query
20−@docs member, lookup, findWithDefault
22+@docs member, get, getOrElse, getOrFail
2123
2224# Combine
2325@docs union, intersect, diff
@@ -26,7 +28,7 @@ Insert, remove, and query operations all take *O(log n)* time.
2628@docs keys, values, toList, fromList
2729
2830# Transform
29−@docs map, foldl, foldr
31+@docs map, foldl, foldr, filter, partition
3032
3133-}
3234
@@ -84,33 +86,74 @@ max t =
8486 RBNode _ _ _ _ r -> max r
8587 RBEmpty _ -> Native.Error.raise "(max Empty) is not defined"
8688
87−{-| Lookup the value associated with a key. -}
88−lookup : comparable -> Dict comparable v -> Maybe v
89−lookup k t =
89+{-| Get the value associated with a key. If the key is not found, return
90+`Nothing`. This is useful when you are not sure if a key will be in the
91+dictionary.
92+
93+ animals = fromList [ ("Tom", Cat), ("Jerry", Mouse) ]
94+
95+ get "Tom" animals == Just Cat
96+ get "Mouse" animals == Just Mouse
97+ get "Spike" animals == Nothing
98+
99+The `getOrElse` and `getOrFail` are built-in ways to handle common ways of
100+using the resulting `Maybe`.
101+-}
102+get : comparable -> Dict comparable v -> Maybe v
103+get k t =
90104 case t of
91105 RBEmpty LBlack -> Nothing
92106 RBNode _ k' v l r ->
93107 case Native.Utils.compare k k' of
94− LT -> lookup k l
108+ LT -> get k l
95109 EQ -> Just v
96− GT -> lookup k r
110+ GT -> get k r
111+
112+{-| Get the value associated with a key. If the key is not found,
113+return a default value.
97114
98−{-| Find the value associated with a key. If the key is not found,
99−return the default value. -}
100−findWithDefault : v -> comparable -> Dict comparable v -> v
101−findWithDefault base k t =
115+ animals = fromList [ ("Tom", Cat), ("Jerry", Mouse) ]
116+
117+ getOrElse Dog "Tom" animals == Cat
118+ getOrElse Dog "Mouse" animals == Mouse
119+ getOrElse Dog "Spike" animals == Dog
120+-}
121+getOrElse : v -> comparable -> Dict comparable v -> v
122+getOrElse base k t =
102123 case t of
103124 RBEmpty LBlack -> base
104125 RBNode _ k' v l r ->
105126 case Native.Utils.compare k k' of
106− LT -> findWithDefault base k l
127+ LT -> getOrElse base k l
107128 EQ -> v
108− GT -> findWithDefault base k r
129+ GT -> getOrElse base k r
130+
131+{-| Get the value associated with a key.
132+
133+ animals = fromList [ ("Tom", Cat), ("Jerry", Mouse) ]
134+
135+ getOrFail "Tom" animals == Cat
136+ getOrFail "Mouse" animals == Mouse
137+ getOrFail "Spike" animals == -- Runtime Error!
138+
139+Warning: this function will result in a runtime error if the key is not found,
140+so it is best to use `get` or `getOrElse` unless you are sure the key will be
141+found.
142+-}
143+getOrFail : comparable -> Dict comparable v -> v
144+getOrFail k t =
145+ case t of
146+ RBEmpty LBlack -> Native.Error.raise "key not found when using 'getOrFail'"
147+ RBNode _ k' v l r ->
148+ case Native.Utils.compare k k' of
149+ LT -> getOrFail k l
150+ EQ -> v
151+ GT -> getOrFail k r
109152
110153{-| Determine if a key is in a dictionary. -}
111154member : comparable -> Dict comparable v -> Bool
112155-- Does t contain k?
113−member k t = isJust <| lookup k t
156+member k t = isJust <| get k t
114157
115158ensureBlackRoot : Dict k v -> Dict k v
116159ensureBlackRoot t =
@@ -327,9 +370,7 @@ union t1 t2 = foldl insert t2 t1
327370{-| Keep a key-value pair when its key appears in the second dictionary.
328371Preference is given to values in the first dictionary. -}
329372intersect : Dict comparable v -> Dict comparable v -> Dict comparable v
330−intersect t1 t2 =
331− let combine k v t = if k `member` t2 then insert k v t else t
332− in foldl combine empty t1
373+intersect t1 t2 = filter (\k _ -> k `member` t2) t1
333374
334375{-| Keep a key-value pair when its key does not appear in the second dictionary.
335376Preference is given to the first dictionary. -}
@@ -351,3 +392,20 @@ toList t = foldr (\k v acc -> (k,v) :: acc) [] t
351392{-| Convert an association list into a dictionary. -}
352393fromList : [(comparable,v)] -> Dict comparable v
353394fromList assocs = List.foldl (\(k,v) d -> insert k v d) empty assocs
395+
396+{-| Keep a key-value pair when it satisfies a predicate. -}
397+filter : (comparable -> v -> Bool) -> Dict comparable v -> Dict comparable v
398+filter p dict =
399+ let add k v t = if p k v then insert k v t else t
400+ in foldl add empty dict
401+
402+{-| Partition a dictionary according to a predicate. The first dictionary
403+contains all key-value pairs which satisfy the predicate, and the second
404+contains the rest.
405+-}
406+partition : (comparable -> v -> Bool) -> Dict comparable v -> (Dict comparable v, Dict comparable v)
407+partition p dict =
408+ let add k v (t1, t2) = if p k v
409+ then (insert k v t1, t2)
410+ else (t1, insert k v t2)
411+ in foldl add (empty, empty) dict
libraries/Graphics/Collage.elm−1
@@ -38,7 +38,6 @@ import Native.Graphics.Collage
3838import Graphics.Element (Element)
3939import Color (Color, black, Gradient)
4040import Maybe (Maybe)
41−import JavaScript (JSString)
4241
4342type Form = {
4443 theta : Float,
libraries/Graphics/Element.elm+10−13
@@ -39,8 +39,6 @@ If you need more precision, you can create custom positions.
3939
4040import Basics (..)
4141import Native.Utils
42−import JavaScript as JS
43−import JavaScript (JSString)
4442import List as List
4543import Color (..)
4644import Maybe (Maybe, Just, Nothing)
@@ -51,8 +49,8 @@ type Properties = {
5149 height : Int,
5250 opacity : Float,
5351 color : Maybe Color,
54− href : JSString,
55− tag : JSString,
52+ href : String,
53+ tag : String,
5654 hover : (),
5755 click : ()
5856 }
@@ -120,21 +118,20 @@ with a link like this: `/facts-about-animals.elm#all-about-badgers`
120118-}
121119tag : String -> Element -> Element
122120tag name e = let p = e.props in
123− { element=e.element, props={p | tag <- JS.fromString name} }
121+ { element=e.element, props={p | tag <- name} }
124122
125123{-| Create an `Element` that is a hyper-link. -}
126124link : String -> Element -> Element
127125link href e = let p = e.props in
128− { element=e.element, props={p | href <- JS.fromString href} }
126+ { element=e.element, props={p | href <- href} }
129127
130−emptyStr = JS.fromString ""
131128newElement w h e =
132− { props = Properties (Native.Utils.guid ()) w h 1 Nothing emptyStr emptyStr () ()
129+ { props = Properties (Native.Utils.guid ()) w h 1 Nothing "" "" () ()
133130 , element = e
134131 }
135132
136133data ElementPrim
137− = Image ImageStyle Int Int JSString
134+ = Image ImageStyle Int Int String
138135 | Container Position Element
139136 | Flow Direction [Element]
140137 | Spacer
@@ -145,13 +142,13 @@ data ImageStyle = Plain | Fitted | Cropped (Int,Int) | Tiled
145142
146143{-| Create an image given a width, height, and image source. -}
147144image : Int -> Int -> String -> Element
148−image w h src = newElement w h (Image Plain w h (JS.fromString src))
145+image w h src = newElement w h (Image Plain w h src)
149146
150147{-| Create a fitted image given a width, height, and image source.
151148This will crop the picture to best fill the given dimensions.
152149-}
153150fittedImage : Int -> Int -> String -> Element
154−fittedImage w h src = newElement w h (Image Fitted w h (JS.fromString src))
151+fittedImage w h src = newElement w h (Image Fitted w h src)
155152
156153{-| Create a cropped image. Take a rectangle out of the picture starting
157154at the given top left coordinate. If you have a 140-by-140 image,
@@ -161,11 +158,11 @@ the following will cut a 100-by-100 square out of the middle of it.
161158-}
162159croppedImage : (Int,Int) -> Int -> Int -> String -> Element
163160croppedImage pos w h src =
164− newElement w h (Image (Cropped pos) w h (JS.fromString src))
161+ newElement w h (Image (Cropped pos) w h src)
165162
166163tiledImage : Int -> Int -> String -> Element
167164tiledImage w h src =
168− newElement w h (Image Tiled w h (JS.fromString src))
165+ newElement w h (Image Tiled w h src)
169166
170167data Three = P | Z | N
171168data Pos = Absolute Int | Relative Float
libraries/Graphics/Input.elm+1−1
@@ -89,7 +89,7 @@ button = Native.Graphics.Input.button
8989
9090 prettyButton : Element
9191 prettyButton =
92− customButton click.handle
92+ customButton click.handle ()
9393 (image 100 40 "/button_up.jpg")
9494 (image 100 40 "/button_hover.jpg")
9595 (image 100 40 "/button_down.jpg")
libraries/JavaScript.elmdeleted−74
@@ -1,74 +0,0 @@
1−
2−module JavaScript where
3−
4−{-|This library does basic conversions between Elm and JS values. This allows
5−the internal data structures of Elm to change and improve with no impact on JS
6−integration.
7−
8−It is intended to be imported as `import JavaScript as JS`. That way functions
9−like `JS.toInt` convert JavaScript *to* Elm integers and functions like
10−`JS.fromString` gets JavaScript *from* Elm strings.
11−
12−# Javascript to Elm
13−@docs toString, toInt, toFloat, toBool, toList
14−
15−# JavaScript from Elm
16−@docs fromString, fromInt, fromFloat, fromBool, fromList
17−-}
18−{-
19−# DOM Nodes and Elements
20−@docs toElement, fromElement
21−-}
22−
23−import Native.JavaScript
24−
25−data JSNumber = JSNumber
26−data JSBool = JSBool
27−data JSString = JSString
28−data JSArray a = JSArray a
29−data JSDomNode = JSDomNode
30−data JSObject = JSObject
31−
32−{-| Requires that the input array be uniform (all members have the same type) -}
33−toList : JSArray a -> [a]
34−toList = Native.JavaScript.toList
35−
36−toInt : JSNumber -> Int
37−toInt = Native.JavaScript.toInt
38−
39−toFloat : JSNumber -> Float
40−toFloat = Native.JavaScript.toFloat
41−
42−toBool : JSBool -> Bool
43−toBool = Native.JavaScript.toBool
44−
45−toString : JSString -> String
46−toString = Native.JavaScript.toString
47−
48−
49−{-| Produces a uniform JavaScript array with all members of the same type. -}
50−fromList : [a] -> JSArray a
51−fromList = Native.JavaScript.fromList
52−
53−fromInt : Int -> JSNumber
54−fromInt = Native.JavaScript.fromInt
55−
56−fromFloat : Float -> JSNumber
57−fromFloat = Native.JavaScript.fromFloat
58−
59−fromBool : Bool -> JSBool
60−fromBool = Native.JavaScript.fromBool
61−
62−fromString : String -> JSString
63−fromString = Native.JavaScript.fromString
64−
65−{-
66−{-| Turn an `Element` into a DOM node. -}
67−fromElement : Element -> JSDomNode
68−fromElement = Native.JavaScript.fromElement
69−
70−{-| Turn a DOM node into an `Element`. You can resize the node
71−using the normal `width` and `height` functions. -}
72−toElement : Int -> Int -> JSDomNode -> Element
73−toElement = Native.JavaScript.toElement
74−-}
libraries/JavaScript/Experimental.elm+31−16
@@ -8,34 +8,49 @@ improve as we find its key failings.
88@docs toRecord, fromRecord
99-}
1010
11−import JavaScript (JSObject)
1211import Native.JavaScript
12+import Native.Json
13+import Json
14+
15+data RawObject = RawObject
1316
1417{-| Turn arbitrary JavaScript objects into Elm records.
1518Arrays are converted into lists, nested objects are allowed.
1619No `null` values or non-homogeneous arrays.
1720
18− -- OK objects
19− { student:"Steve", scores:[83,94,99,72] }
20− { errorLevel:10, critical:true }
21+ -- OK objects
22+ { student:"Steve", scores:[83,94,99,72] }
23+ { errorLevel:10, critical:true }
2124
22− -- BAD objects
23− { answer:null }
24− { info:[true,42,'what'] }
25+ -- BAD objects
26+ { answer:null }
27+ { info:[true,42,'what'] }
2528-}
26−toRecord : JSObject -> a
29+toRecord : RawObject -> a
2730toRecord = Native.JavaScript.toRecord
2831
2932{-| Turn arbitrary Elm records into JavaScript objects.
3033Lists become arrays, nested records are allowed. No ADTs.
3134
32− -- OK records
33− { student="Steve", scores=[83,94,99,72] }
34− { errorLevel=10, critical=True }
35+ -- OK records
36+ { student="Steve", scores=[83,94,99,72] }
37+ { errorLevel=10, critical=True }
38+
39+ -- BAD records
40+ { answer = Nothing }
41+ { result = Left "An error occurred" }
42+-}
43+fromRecord : a -> RawObject
44+fromRecord = Native.JavaScript.fromRecord
45+
46+{-| Turn a raw JavaScript object into a `Json.Value` as described in the
47+`Json` library.
48+-}
49+toJson : RawObject -> Json.Value
50+toJson = Native.Json.fromJS
3551
36− -- BAD records
37− { answer = Nothing }
38− { result = Left "An error occurred" }
52+{-| Turn a `Json.Value` (as described in the `Json` library) into a raw
53+JavaScript object.
3954-}
40−fromRecord : a -> JSObject
41−fromRecord = Native.JavaScript.fromRecord
55+fromJson : Json.Value -> RawObject
56+fromJson = Native.Json.toJS
libraries/Json.elm+25−110
@@ -3,132 +3,47 @@ module Json where
33
44{-| Library for working with [JSON](https://en.wikipedia.org/wiki/JSON) values.
55
6−# Json Values
7−@docs JsonValue
8−
9−# Strings
10−@docs fromString, toString
11−
12−# JS Strings
13−@docs fromJSString, toJSString
14−
15−# JS Objects
16−@docs fromJSObject, toJSObject
6+@docs Value, fromString, toString
177
188-}
199
20−import Basics (..)
2110import Dict
2211import Maybe (Maybe)
23−import JavaScript as JS
2412import Native.Json
25−import JavaScript (JSObject, JSString)
2613
27−{-| This datatype can represent all valid values that can be held in a JSON
28−object. In Elm, a proper JSON object is represented as a (Dict String JsonValue)
29−which is a mapping from strings to Json Values. -}
30−data JsonValue
14+{-| This datatype can represent all valid JSON values. A `Value` can be sent
15+through ports, and the runtime will do a conversion like this:
16+
17+ [2,'abc',null] == Array [Number 2, String "abc", Null]
18+
19+JSON is an intentional subset of JavaScript values which excludes functions.
20+This ensures that impure functions cannot sneak into Elm through ports.
21+-}
22+data Value
3123 = String String
3224 | Number Float
3325 | Boolean Bool
3426 | Null
35− | Array [JsonValue]
36− | Object (Dict.Dict String JsonValue)
37−
27+ | Array [Value]
28+ | Object (Dict.Dict String Value)
3829
39−-- String Converters
4030
41−{-| Convert a `JsonValue` into a prettified string.
31+{-| Convert a `Value` into a prettified string.
4232The first argument is a separator token (e.g. \" \", \"\\n\", etc.) that will
43−be used for indentation in the prettified string version of the JSON. -}
44−toString : String -> JsonValue -> String
45−toString sep v = JS.toString (Native.Json.toJSString sep v)
33+be used for indentation in the prettified string version of the JSON.
4634
47−{-| Convert a proper JSON object into a JavaScript string.
48−Note that the type JSString seen here is not the same as the type constructor
49−JsonString used elsewhere in this module. -}
50−toJSString : String -> JsonValue -> JSString
51−toJSString = Native.Json.toJSString
35+ toString "" Null == "null"
36+ toString "" (Number 4.0) == "4.0"
37+-}
38+toString : String -> Value -> String
39+toString sep value = Native.Json.toString sep value
5240
5341{-| Parse a string representation of a proper JSON object into
54−its Elm representation. -}
55−fromString : String -> Maybe JsonValue
56−fromString s = Native.Json.fromJSString (JS.fromString s)
57−
58−{-| Parse a JavaScript string representation of a proper JSON object into
59−its Elm representation. -}
60−fromJSString : JSString -> Maybe JsonValue
61−fromJSString = Native.Json.fromJSString
62−
63−{-| Convert a JS object into a `JsonValue`. -}
64−fromJSObject : JSObject -> JsonValue
65−fromJSObject = Native.Json.fromJSObject
42+its Elm representation.
6643
67−{-| Convert a `JsonValue` into a `JSObject`. Paired with the
68−[`JavaScript.Experimental` library](/docs/JavaScript/Experimental.elm),
69−This lets you convert strings into Elm records:
70−
71− import JavaScript.Experimental as JS
72−
73− stringToRecord str =
74− case fromString str of
75− Just jsonValue -> Just (JS.toRecord (toJSObject jsonValue))
76− Nothing -> Nothing
44+ fromString "null" == Just Null
45+ fromString "[true,3]" == Just (Array [Boolean True, Number 3])
46+ fromString "{'abc':4}" == Just (Object (Dict.fromList [("abc", Number 4)]))
7747-}
78−toJSObject : JsonValue -> JSObject
79−toJSObject = Native.Json.toJSObject
80−
81− {-- Extract Elm values from Json values
82−
83− string : JsonValue -> String
84− string v = case v of { String s -> s ; _ -> "" }
85−
86− number : JsonValue -> Float
87− number v = case v of { Number n -> n ; _ -> 0 }
88−
89− boolean : JsonValue -> Bool
90− boolean v = case v of { Boolean b -> b ; _ -> False }
91−
92− array : JsonValue -> [JsonValue]
93− array v = case v of { Array a -> a ; _ -> [] }
94−
95− object : JsonValue -> Dict String JsonValue
96− object v = case v of { Object o -> o ; _ -> Dict.empty }
97−
98−
99− -- Extract Elm values from dictionaries of Json values
100−
101− {-| Find a value in a Json Object using the passed get function. If the key is
102− not found, this returns the given default/base value. -}
103− find get base =
104− let f key dict =
105− case Dict.lookup key dict of
106− Nothing -> base
107− Just v -> get v
108− in f
109−
110− {-| Find a string value in an Elm Json object. If the key is not found or the
111− value found is not a string, this returns the empty string. -}
112− findString : String -> Object -> String
113− findString = find string ""
114−
115− {-| Find a number value in an Elm Json object. If the key is not found or the
116− value found is not a number, this returns 0 -}
117− findNumber : String -> Object -> Float
118− findNumber = find number 0
119−
120− {-| Find a boolean value in an Elm Json object. If the key is not found or the
121− value found is not a boolean, this returns the False. -}
122− findBoolean : String -> Object -> Bool
123− findBoolean = find boolean False
124−
125− {-| Find an array value in an Elm Json object. If the key is not found or the
126− value found is not an array, this returns an empty list. -}
127− findArray : String -> Object -> [JsonValue]
128− findArray = find array []
129−
130− {-| Find an object value in an Elm Json object. If the key is not found or the
131− value found is not an object, this returns an empty object. -}
132− findObject : String -> Object -> Object
133− findObject = find object Dict.empty
134− --}
48+fromString : String -> Maybe Value
49+fromString str = Native.Json.fromString str
libraries/List.elm+25−15
@@ -163,13 +163,19 @@ maximum = foldl1 max
163163minimum : [comparable] -> comparable
164164minimum = foldl1 min
165165
166−{-| Split a list based on the predicate. -}
166+{-| Partition a list based on a predicate. The first list contains all values
167+that satisfy the predicate, and the second list contains all the value that do
168+not.
169+
170+ partition (\x -> x < 3) [0..5] == ([0,1,2], [3,4,5])
171+ partition isEven [0..5] == ([0,2,4], [1,3,5])
172+-}
167173partition : (a -> Bool) -> [a] -> ([a],[a])
168−partition pred lst =
169− case lst of
170− [] -> ([],[])
171− x::xs -> let (bs,cs) = partition pred xs in
172− if pred x then (x::bs,cs) else (bs,x::cs)
174+partition pred =
175+ let step x (ts, fs) = if pred x
176+ then (x::ts, fs)
177+ else (ts, x::fs)
178+ in foldr step ([],[])
173179
174180{-| Combine two lists, combining them into tuples pairwise.
175181If one list is longer, the extra elements are dropped.
@@ -188,12 +194,14 @@ If one list is longer, the extra elements are dropped.
188194zipWith : (a -> b -> c) -> [a] -> [b] -> [c]
189195zipWith = Native.List.zipWith
190196
191−{-| Decompose a list of tuples. -}
197+{-| Decompose a list of tuples.
198+
199+ unzip [(0, True), (17, False), (1337, True)] == ([0,17,1337], [True,False,True])
200+-}
192201unzip : [(a,b)] -> ([a],[b])
193−unzip pairs =
194− case pairs of
195− [] -> ([],[])
196− (x,y)::ps -> let (xs,ys) = (unzip ps) in (x::xs,y::ys)
202+unzip =
203+ let step (x,y) (xs, ys) = (x::xs, y::ys)
204+ in foldr step ([], [])
197205
198206{-| Places the given value between all of the lists in the second
199207argument and concatenates the result.
@@ -209,10 +217,12 @@ join = Native.List.join
209217-}
210218intersperse : a -> [a] -> [a]
211219intersperse sep xs =
212− case xs of
213− a::b::cs -> a :: sep :: intersperse sep (b::cs)
214− [a] -> [a]
215− [] -> []
220+ case xs of
221+ [] -> []
222+ hd::tl ->
223+ let step x rest = sep :: x :: rest
224+ spersed = foldr step [] tl
225+ in hd :: spersed
216226
217227{-| Take the first n members of a list: `(take 2 [1,2,3,4] == [1,2])` -}
218228take : Int -> [a] -> [a]
libraries/Native/Array.jsnew file+597
@@ -0,0 +1,597 @@
1+Elm.Native.Array = {};
2+Elm.Native.Array.make = function(elm) {
3+ elm.Native = elm.Native || {};
4+ elm.Native.Array = elm.Native.Array || {};
5+ if (elm.Native.Array.values) return elm.Native.Array.values;
6+ if ('values' in Elm.Native.Array)
7+ return elm.Native.Array.values = Elm.Native.Array.values;
8+
9+ var List = Elm.Native.List.make(elm);
10+
11+ // A RRB-Tree has two distinct data types.
12+ // Leaf -> "height" is always 0
13+ // "table" is an array of elements
14+ // Node -> "height" is always greater than 0
15+ // "table" is an array of child nodes
16+ // "lengths" is an array of accumulated lengths of the child nodes
17+
18+ // M is the maximal table size. 32 seems fast. E is the allowed increase
19+ // of search steps when concatting to find an index. Lower values will
20+ // decrease balancing, but will increase search steps.
21+ var M = 32;
22+ var E = 2;
23+
24+ // An empty array.
25+ var empty = { ctor:"_Array", height:0, table:new Array() };
26+
27+ function get(i, array) {
28+ if (i < 0 || i >= length(array)) {
29+ throw new Error("Index " + i + " is out of range. Check the length of " +
30+ "your array first or use getMaybe or getWithDefault.");
31+ }
32+ return unsafeGet(i, array);
33+ }
34+
35+ function unsafeGet(i, array) {
36+ for (var x = array.height; x > 0; x--) {
37+ var slot = i >> (x * 5);
38+ if (slot > 0) {
39+ while (array.lengths[slot - 1] > i) { slot--; }
40+ i -= array.lengths[slot - 1];
41+ }
42+ array = array.table[slot];
43+ }
44+ return array.table[i];
45+ }
46+
47+ // Sets the value at the index i. Only the nodes leading to i will get
48+ // copied and updated.
49+ function set(i, item, array) {
50+ if (i < 0 || length(array) <= i) {
51+ return array;
52+ }
53+ return unsafeSet(i, item, array);
54+ }
55+
56+ function unsafeSet(i, item, array) {
57+ array = nodeCopy(array);
58+
59+ if (array.height == 0) {
60+ array.table[i] = item;
61+ } else {
62+ var slot = getSlot(i, array);
63+ if (slot > 0) {
64+ i -= array.lengths[slot - 1];
65+ }
66+ array.table[slot] = unsafeSet(i, item, array.table[slot]);
67+ }
68+ return array;
69+ }
70+
71+ function initialize(len, f) {
72+ if (len == 0) { return empty; }
73+ var h = Math.floor(Math.log(len)/Math.log(M));
74+ return initialize_(f, h, 0, len);
75+ }
76+
77+ function initialize_(f, h, from, to) {
78+ if (h == 0) {
79+ var table = new Array((to - from) % (M + 1));
80+ for (var i = 0; i < table.length; i++) {
81+ table[i] = f(from + i);
82+ }
83+ return { ctor:"_Array", height:0, table:table };
84+ }
85+
86+ var step = Math.pow(M, h);
87+ var table = new Array(Math.ceil((to - from) / step));
88+ var lengths = new Array(table.length);
89+ for (var i = 0; i < table.length; i++) {
90+ table[i] = initialize_( f, h - 1, from + (i * step)
91+ , Math.min(from + ((i + 1) * step), to));
92+ lengths[i] = length(table[i]) + (i > 0 ? lengths[i-1] : 0);
93+ }
94+ return { ctor:"_Array", height:h, table:table, lengths:lengths };
95+ }
96+
97+ function fromList(list) {
98+ if (list == List.Nil) { return empty; }
99+
100+ // Allocate M sized blocks (table) and write list elements to it.
101+ var table = new Array(M);
102+ var nodes = new Array();
103+ var i = 0;
104+
105+ while (list.ctor !== '[]') {
106+ table[i] = list._0;
107+ list = list._1;
108+ i++;
109+
110+ // table is full, so we can push a leaf containing it into the
111+ // next node.
112+ if (i == M) {
113+ fromListPush({ ctor:"_Array", height:0, table:table }
114+ , nodes);
115+ table = new Array(M);
116+ i = 0;
117+ }
118+ }
119+
120+ // Maybe there is something left on the table.
121+ if (i > 0) {
122+ fromListPush({ ctor:"_Array", height:0, table:table.splice(0,i) }
123+ , nodes);
124+ }
125+
126+ // Go through all of the nodes and eventually push them into higher nodes.
127+ for (var h = 0; h < nodes.length - 1; h++) {
128+ if (nodes[h].table.length > 0) {
129+ fromListPush(nodes[h], nodes);
130+ }
131+ }
132+
133+ var head = nodes[nodes.length - 1];
134+ if (head.height > 0 && head.table.length == 1) {
135+ return head.table[0];
136+ } else {
137+ return head;
138+ }
139+ }
140+
141+ // Push a node into a higher node as a child.
142+ function fromListPush(toPush, nodes) {
143+ var h = toPush.height;
144+
145+ // Maybe the node on this height does not exist.
146+ if (nodes.length == h) {
147+ nodes.push({ ctor:"_Array", height:h + 1
148+ , table:new Array()
149+ , lengths:new Array() });
150+ }
151+
152+ nodes[h].table.push(toPush);
153+ var len = length(toPush);
154+ if (nodes[h].lengths.length > 0) {
155+ len += nodes[h].lengths[nodes[h].lengths.length - 1];
156+ }
157+ nodes[h].lengths.push(len);
158+
159+ if (nodes[h].table.length == M) {
160+ fromListPush(nodes[h], nodes);
161+ nodes[h] = { ctor:"_Array", height:h + 1
162+ , table:new Array()
163+ , lengths:new Array() };
164+ }
165+ }
166+
167+ // Pushes an item via push_ to the bottom right of a tree.
168+ function push(item, a) {
169+ var pushed = push_(item, a);
170+ if (pushed !== null) {
171+ return pushed;
172+ }
173+
174+ var newTree = create(item, a.height);
175+ return siblise(a, newTree);
176+ }
177+
178+ // Recursively tries to push an item to the bottom-right most
179+ // tree possible. If there is no space left for the item,
180+ // null will be returned.
181+ function push_(item, a) {
182+ // Handle resursion stop at leaf level.
183+ if (a.height == 0) {
184+ if (a.table.length < M) {
185+ var newA = { ctor:"_Array", height:0, table:a.table.slice() };
186+ newA.table.push(item);
187+ return newA;
188+ } else {
189+ return null;
190+ }
191+ }
192+
193+ // Recursively push
194+ var pushed = push_(item, botRight(a));
195+
196+ // There was space in the bottom right tree, so the slot will
197+ // be updated.
198+ if (pushed != null) {
199+ var newA = nodeCopy(a);
200+ newA.table[newA.table.length - 1] = pushed;
201+ newA.lengths[newA.lengths.length - 1]++;
202+ return newA
203+ }
204+
205+ // When there was no space left, check if there is space left
206+ // for a new slot with a tree which contains only the item
207+ // at the bottom.
208+ if (a.table.length < M) {
209+ var newSlot = create(item, a.height - 1);
210+ var newA = nodeCopy(a);
211+ newA.table.push(newSlot);
212+ newA.lengths.push(newA.lengths[newA.lengths.length - 1] + length(newSlot));
213+ return newA
214+ } else {
215+ return null;
216+ }
217+ }
218+
219+ // Converts an array into a list of elements.
220+ function toList(a) {
221+ return toList_(List.Nil, a);
222+ }
223+
224+ function toList_(list, a) {
225+ for (var i = a.table.length - 1; i >= 0; i--) {
226+ list = a.height == 0 ? List.Cons(a.table[i], list) : toList_(list, a.table[i]);
227+ }
228+ return list;
229+ }
230+
231+ // Maps a function over the elements of an array.
232+ function map(f, a) {
233+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
234+ if (a.height > 0) { newA.lengths = a.lengths; }
235+ for (var i = 0; i < a.table.length; i++) {
236+ newA.table[i] = a.height == 0 ? f(a.table[i]) : map(f, a.table[i]);
237+ }
238+ return newA;
239+ }
240+
241+ // Maps a function over the elements with their index as first argument.
242+ function indexedMap(f, a) {
243+ var newA = { ctor:"_Array", height:a.height, table:new Array(a.table) };
244+ if (a.height > 0) { newA.lengths = a.lengths; }
245+ for (var i = 0; i < a.table.length; i++) {
246+ newA.table[i] = a.height == 0 ? A2(f, i, a.table[i]) : indexedMap(f, a.table[i]);
247+ }
248+ return newA;
249+ }
250+
251+ function foldl(f, b, a) {
252+ for (var i = 0; i < a.table.length; i++) {
253+ b = A2(f, a.height == 0 ? a.table[i] : foldl(f, b, a.table[i]), b);
254+ }
255+ return b;
256+ }
257+
258+ function foldr(f, b, a) {
259+ for (var i = a.table.length; i--; ) {
260+ b = A2(f, a.height == 0 ? a.table[i] : foldr(f, b, a.table[i]), b);
261+ }
262+ return b;
263+ }
264+
265+ // Returns a sliced tree. "to" is inclusive, but this may change,
266+ // when I understand, why e.g. JS does not handle it this way. :-)
267+ // If "from" or "to" is negative, they will select from the end on.
268+ // TODO: currently, it slices the right, then the left. This can be
269+ // optimized.
270+ function slice(from, to, a) {
271+ if (from < 0) { from += length(a); }
272+ if (to < 0) { to += length(a); }
273+ return sliceLeft(from, sliceRight(to, a));
274+ }
275+
276+ function sliceRight(to, a) {
277+ if (to == length(a)) {
278+ return a;
279+ }
280+
281+ // Handle leaf level.
282+ if (a.height == 0) {
283+ var newA = { ctor:"_Array", height:0 };
284+ newA.table = a.table.slice(0, to + 1);
285+ return newA;
286+ }
287+
288+ // Slice the right recursively.
289+ var right = getSlot(to, a);
290+ var sliced = sliceRight(to - (right > 0 ? a.lengths[right - 1] : 0), a.table[right]);
291+
292+ // Maybe the a node is not even needed, as sliced contains the whole slice.
293+ if (right == 0) {
294+ return sliced;
295+ }
296+
297+ // Create new node.
298+ var newA = { ctor:"_Array", height:a.height
299+ , table:a.table.slice(0, right + 1)
300+ , lengths:a.lengths.slice(0, right + 1) };
301+ newA.table[right] = sliced;
302+ newA.lengths[right] = length(sliced) + (right > 0 ? newA.lengths[right - 1] : 0);
303+ return newA;
304+ }
305+
306+ function sliceLeft(from, a) {
307+ if (from == 0) {
308+ return a;
309+ }
310+
311+ // Handle leaf level.
312+ if (a.height == 0) {
313+ var newA = { ctor:"_Array", height:0 };
314+ newA.table = a.table.slice(from, a.table.length + 1);
315+ return newA;
316+ }
317+
318+ // Slice the left recursively.
319+ var left = getSlot(from, a);
320+ var sliced = sliceLeft(from - (left > 0 ? a.lengths[left - 1] : 0), a.table[left]);
321+
322+ // Maybe the a node is not even needed, as sliced contains the whole slice.
323+ if (left == a.table.length - 1) {
324+ return sliced;
325+ }
326+
327+ // Create new node.
328+ var newA = { ctor:"_Array", height:a.height
329+ , table:a.table.slice(left, a.table.length + 1)
330+ , lengths:new Array(a.table.length - left) };
331+ newA.table[left] = sliced;
332+ var len = 0;
333+ for (var i = 0; i < newA.table.length; i++) {
334+ len += length(newA.table[i]);
335+ newA.lengths[i] = len;
336+ }
337+
338+ return newA;
339+ }
340+
341+ // Appends two trees.
342+ // TODO: Add support for appending trees of different sizes. Current
343+ // behavior will just rise the lower tree and then append them.
344+ function append(a,b) {
345+ if (b.height > a.height) { return append(parentise(a, b.height), b); }
346+ if (a.height > b.height) { return append(a, parentise(b, a.height)); }
347+ if (a.height == 0) { return append(parentise(a, 1), parentise(b, 1)); }
348+
349+ var c = append_(a, b);
350+ if (c[1].table.length > 0) {
351+ return siblise(c[0], c[1]);
352+ } else {
353+ return c[0];
354+ }
355+ }
356+
357+ // Returns an array of two nodes. The second node _may_ be empty. This case
358+ // needs to be handled by the function, that called append_. May be only
359+ // called for trees with an minimal height of 1.
360+ function append_(a, b) {
361+ if (a.height == 1) {
362+ // Check if balancing is needed and return based on that.
363+ var toRemove = calcToRemove(a, b);
364+ if (toRemove <= E) {
365+ return [a,b];
366+ }
367+
368+ return shuffle(a, b, toRemove);
369+ }
370+
371+ var appended = append_(botRight(a), botLeft(b));
372+ a = nodeCopy(a), b = nodeCopy(b);
373+
374+ // Adjust the bottom right side of the new tree.
375+ a.table[a.table.length - 1] = appended[0];
376+ a.lengths[a.lengths.length - 1] = length(appended[0])
377+ a.lengths[a.lengths.length - 1] += a.lengths.length > 1 ? a.lengths[a.lengths.length - 2] : 0;
378+
379+ // Adjust the bottom left side of the new tree.
380+ if (appended[1].table.length > 0) {
381+ b.table[0] = appended[1];
382+ b.lengths[0] = length(appended[1]);
383+ for (var i = 1, len = length(b.table[0]); i < b.lengths.length; i++) {
384+ len += length(b.table[i]);
385+ b.lengths[i] = len;
386+ }
387+ } else {
388+ b.table.shift();
389+ for (var i = 1; i < b.lengths.length; i++) {
390+ b.lengths[i] = b.lengths[i] - b.lengths[0];
391+ }
392+ b.lengths.shift();
393+ }
394+
395+ // Check if balancing is needed and return based on that.
396+ var toRemove = calcToRemove(a, b);
397+ if (toRemove <= E || b.table.length == 0) {
398+ return [a,b];
399+ }
400+
401+ return shuffle(a, b, toRemove);
402+ }
403+
404+ // Returns the extra search steps for E. Refer to the paper.
405+ function calcToRemove(a, b) {
406+ var subLengths = 0;
407+ for (var i = 0; i < a.table.length; i++) {
408+ subLengths += a.table[i].table.length;
409+ }
410+ for (var i = 0; i < b.table.length; i++) {
411+ subLengths += b.table[i].table.length;
412+ }
413+
414+ var toRemove = a.table.length + b.table.length
415+ return toRemove - (Math.floor((subLengths - 1) / M) + 1);
416+ }
417+
418+ // get2, set2 and saveSlot are helpers for accessing elements over two arrays.
419+ function get2(a, b, index) {
420+ return index < a.length ? a[index] : b[index - a.length];
421+ }
422+
423+ function set2(a, b, index, value) {
424+ if (index < a.length) {
425+ a[index] = value;
426+ } else {
427+ b[index - a.length] = value;
428+ }
429+ }
430+
431+ function saveSlot(a, b, index, slot) {
432+ set2(a.table, b.table, index, slot);
433+
434+ var l = (index == 0 || index == a.lengths.length) ?
435+ 0 : get2(a.lengths, a.lengths, index - 1);
436+ set2(a.lengths, b.lengths, index, l + length(slot));
437+ }
438+
439+ // Creates a node or leaf with a given length at their arrays for perfomance.
440+ // Is only used by shuffle.
441+ function createNode(h, length) {
442+ if (length < 0) { length = 0; }
443+ var a = { ctor:"_Array", height:h, table:new Array(length) };
444+ if (h > 0) {
445+ a.lengths = new Array(length);
446+ }
447+ return a;
448+ }
449+
450+ // Returns an array of two balanced nodes.
451+ function shuffle(a, b, toRemove) {
452+ var newA = createNode(a.height, Math.min(M, a.table.length + b.table.length - toRemove));
453+ var newB = createNode(a.height, newA.table.length - (a.table.length + b.table.length - toRemove));
454+
455+ // Skip the slots with size M. More precise: copy the slot references
456+ // to the new node
457+ var read = 0;
458+ while (get2(a.table, b.table, read).table.length % M == 0) {
459+ set2(newA.table, newB.table, read, get2(a.table, b.table, read));
460+ set2(newA.lengths, newB.lengths, read, get2(a.lengths, b.lengths, read));
461+ read++;
462+ }
463+
464+ // Pulling items from left to right, caching in a slot before writing
465+ // it into the new nodes.
466+ var write = read;
467+ var slot = new createNode(a.height - 1, 0);
468+ var from = 0;
469+
470+ // If the current slot is still containing data, then there will be at
471+ // least one more write, so we do not break this loop yet.
472+ while (read - write - (slot.table.length > 0 ? 1 : 0) < toRemove) {
473+ // Find out the max possible items for copying.
474+ var source = get2(a.table, b.table, read);
475+ var to = Math.min(M - slot.table.length, source.table.length)
476+
477+ // Copy and adjust size table.
478+ slot.table = slot.table.concat(source.table.slice(from, to));
479+ if (slot.height > 0) {
480+ var len = slot.lengths.length;
481+ for (var i = len; i < len + to - from; i++) {
482+ slot.lengths[i] = length(slot.table[i]);
483+ slot.lengths[i] += (i > 0 ? slot.lengths[i - 1] : 0);
484+ }
485+ }
486+
487+ from += to;
488+
489+ // Only proceed to next slots[i] if the current one was
490+ // fully copied.
491+ if (source.table.length <= to) {
492+ read++; from = 0;
493+ }
494+
495+ // Only create a new slot if the current one is filled up.
496+ if (slot.table.length == M) {
497+ saveSlot(newA, newB, write, slot);
498+ slot = createNode(a.height - 1,0);
499+ write++;
500+ }
501+ }
502+
503+ // Cleanup after the loop. Copy the last slot into the new nodes.
504+ if (slot.table.length > 0) {
505+ saveSlot(newA, newB, write, slot);
506+ write++;
507+ }
508+
509+ // Shift the untouched slots to the left
510+ while (read < a.table.length + b.table.length ) {
511+ saveSlot(newA, newB, write, get2(a.table, b.table, read));
512+ read++; write++;
513+ }
514+
515+ return [newA, newB];
516+ }
517+
518+ // Navigation functions
519+ function botRight(a) { return a.table[a.table.length - 1]; }
520+ function botLeft(a) { return a.table[0]; }
521+
522+ // Copies a node for updating. Note that you should not use this if
523+ // only updating only one of "table" or "lengths" for performance reasons.
524+ function nodeCopy(a) {
525+ var newA = { ctor:"_Array", height:a.height
526+ , table:a.table.slice() };
527+ if (a.height > 0) { newA.lengths = a.lengths.slice(); }
528+ return newA;
529+ }
530+
531+ // Returns how many items are in the tree.
532+ function length(array) {
533+ if (array.height == 0) {
534+ return array.table.length;
535+ } else {
536+ return array.lengths[array.lengths.length - 1];
537+ }
538+ }
539+
540+ // Calculates in which slot of "table" the item probably is, then
541+ // find the exact slot via forward searching in "lengths". Returns the index.
542+ function getSlot(i, a) {
543+ var slot = i >> (5 * a.height);
544+ while (a.lengths[slot - 1] > i) { slot--; }
545+ return slot;
546+ }
547+
548+ // Recursively creates a tree with a given height containing
549+ // only the given item.
550+ function create(item, h) {
551+ if (h == 0) {
552+ return { ctor:"_Array", height:0
553+ , table:[item] };
554+ } else {
555+ return { ctor:"_Array", height:h
556+ , table:[create(item, h - 1)]
557+ , lengths:[1] };
558+ }
559+ }
560+
561+ // Recursively creates a tree that contains the given tree.
562+ function parentise(tree, h) {
563+ if (h == tree.height) {
564+ return tree;
565+ } else {
566+ return { ctor:"_Array", height:h
567+ , table:[parentise(tree, h - 1)]
568+ , lengths:[length(tree)] };
569+ }
570+ }
571+
572+ // Emphasizes blood brotherhood beneath two trees.
573+ function siblise(a, b) {
574+ return { ctor:"_Array", height:a.height + 1
575+ , table:[a, b]
576+ , lengths:[length(a), length(a) + length(b)] };
577+ }
578+
579+ Elm.Native.Array.values = {
580+ empty:empty,
581+ fromList:fromList,
582+ toList:toList,
583+ initialize:F2(initialize),
584+ append:F2(append),
585+ push:F2(push),
586+ slice:F3(slice),
587+ get:F2(get),
588+ set:F3(set),
589+ map:F2(map),
590+ indexedMap:F2(indexedMap),
591+ foldl:F3(foldl),
592+ foldr:F3(foldr),
593+ length:length
594+ };
595+
596+ return elm.Native.Array.values = Elm.Native.Array.values;
597+}
libraries/Native/Date.js+1−2
@@ -4,12 +4,11 @@ Elm.Native.Date.make = function(elm) {
44 elm.Native.Date = elm.Native.Date || {};
55 if (elm.Native.Date.values) return elm.Native.Date.values;
66
7− var JS = Elm.JavaScript.make(elm);
87 var Maybe = Elm.Maybe.make(elm);
98
109 function dateNow() { return new window.Date; }
1110 function readDate(str) {
12− var d = new window.Date(JS.fromString(str));
11+ var d = new window.Date(str);
1312 if (isNaN(d.getTime())) return Maybe.Nothing;
1413 return Maybe.Just(d);
1514 }
libraries/Native/Graphics/Input.js+9−38
@@ -12,7 +12,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
1212 var Text = Elm.Native.Text.make(elm);
1313 var Signal = Elm.Signal.make(elm);
1414 var newElement = Elm.Graphics.Element.make(elm).newElement;
15− var JS = Elm.Native.JavaScript.make(elm);
15+ var List = Elm.Native.List.make(elm);
1616 var Utils = Elm.Native.Utils.make(elm);
1717 var Tuple2 = Utils.Tuple2;
1818
@@ -23,14 +23,14 @@ Elm.Native.Graphics.Input.make = function(elm) {
2323
2424 function renderDropDown(signal, values) {
2525 return function(_) {
26− var entries = JS.fromList(values);
26+ var entries = List.toArray(values);
2727
2828 var drop = newNode('select');
2929 drop.style.border = '0 solid';
3030 drop.style.pointerEvents = 'auto';
3131 for (var i = 0; i < entries.length; ++i) {
3232 var option = newNode('option');
33− var name = JS.fromString(entries[i]._0);
33+ var name = entries[i]._0;
3434 option.value = name;
3535 option.innerHTML = name;
3636 drop.appendChild(option);
@@ -91,7 +91,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
9191 type: 'Button',
9292 render: renderButton,
9393 update: updateButton,
94− model: { signal:signal, value:value, text:JS.fromString(text) }
94+ model: { signal:signal, value:value, text:text }
9595 });
9696 }
9797
@@ -134,13 +134,6 @@ Elm.Native.Graphics.Input.make = function(elm) {
134134
135135 btn.appendChild(btn.elm_up);
136136
137− var clicker = newNode('div');
138− clicker.style.width = btn.elm_up.style.width;
139− clicker.style.height = btn.elm_up.style.height;
140− clicker.style.position = 'absolute';
141− clicker.style.top = 0;
142− btn.appendChild(clicker);
143−
144137 return btn;
145138 }
146139
@@ -266,8 +259,8 @@ Elm.Native.Graphics.Input.make = function(elm) {
266259 field.style.pointerEvents = 'auto';
267260
268261 field.type = model.type;
269− field.placeholder = JS.fromString(model.placeHolder);
270− field.value = JS.fromString(model.content.string);
262+ field.placeholder = model.placeHolder;
263+ field.value = model.content.string;
271264
272265 field.elm_signal = model.signal;
273266 field.elm_handler = model.handler;
@@ -287,7 +280,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
287280
288281 elm.notify(field.elm_signal.id, field.elm_handler({
289282 _:{},
290− string: JS.toString(next),
283+ string: next,
291284 selection: {
292285 _:{},
293286 start: start,
@@ -297,29 +290,7 @@ Elm.Native.Graphics.Input.make = function(elm) {
297290 }));
298291 }
299292
300− function mouseUpdate(event) {
301− var direction = field.selectionDirection === 'forward' ? 'Forward' : 'Backward';
302− elm.notify(field.elm_signal.id, field.elm_handler({
303− _:{},
304− string: field.value,
305− selection: {
306− _:{},
307− start: field.selectionStart,
308− end: field.selectionEnd,
309− direction: { ctor: direction }
310− },
311− }));
312− }
313− function mousedown(event) {
314− mouseUpdate(event);
315− elm.node.addEventListener('mouseup', mouseup);
316− }
317− function mouseup(event) {
318− mouseUpdate(event);
319− elm.node.removeEventListener('mouseup', mouseup)
320− }
321293 field.addEventListener('input', inputUpdate);
322− field.addEventListener('mousedown', mousedown);
323294 field.addEventListener('focus', function() {
324295 field.elm_hasFocus = true;
325296 });
@@ -338,8 +309,8 @@ Elm.Native.Graphics.Input.make = function(elm) {
338309 field.elm_handler = newModel.handler;
339310
340311 field.type = newModel.type;
341− field.placeholder = JS.fromString(newModel.placeHolder);
342− var value = JS.fromString(newModel.content.string);
312+ field.placeholder = newModel.placeHolder;
313+ var value = newModel.content.string;
343314 field.value = value;
344315 field.elm_old_value = value;
345316 if (field.elm_hasFocus) {
libraries/Native/JavaScript.js+59−91
@@ -1,100 +1,68 @@
11Elm.Native.JavaScript = {};
22Elm.Native.JavaScript.make = function(elm) {
3− elm.Native = elm.Native || {};
4− elm.Native.JavaScript = elm.Native.JavaScript || {};
5− if (elm.Native.JavaScript.values) return elm.Native.JavaScript.values;
3+ elm.Native = elm.Native || {};
4+ elm.Native.JavaScript = elm.Native.JavaScript || {};
5+ if (elm.Native.JavaScript.values) return elm.Native.JavaScript.values;
66
7− var List = Elm.Native.List.make(elm);
8− var Render = ElmRuntime.use(ElmRuntime.Render.Element);
7+ var List = Elm.Native.List.make(elm);
8+ var Render = ElmRuntime.use(ElmRuntime.Render.Element);
99
10− function fromJS(v) {
11− var type = typeof v;
12− if (type === 'number' ) return v;
13− if (type === 'boolean') return v;
14− if (type === 'string' ) return v;
15− if (v instanceof Array) {
16− var arr = [];
17− var len = v.length;
18− for (var i = 0; i < len; ++i) {
19− var x = fromJS(v[i]);
20− if (x !== null) arr.push(x);
21− }
22− return List.fromArray(arr);
23− }
24− if (type === 'object') {
25− var rec = { _:{} };
26− for (var f in v) {
27− var x = fromJS(v[f]);
28− if (x !== null) rec[f] = x;
29− }
30− return rec;
31− }
32− return null;
33− }
10+ function fromJS(v) {
11+ var type = typeof v;
12+ if (type === 'number' ) return v;
13+ if (type === 'boolean') return v;
14+ if (type === 'string' ) return v;
15+ if (v instanceof Array) {
16+ var arr = [];
17+ var len = v.length;
18+ for (var i = 0; i < len; ++i) {
19+ var x = fromJS(v[i]);
20+ if (x !== null) arr.push(x);
21+ }
22+ return List.fromArray(arr);
23+ }
24+ if (type === 'object') {
25+ var rec = { _:{} };
26+ for (var f in v) {
27+ var x = fromJS(v[f]);
28+ if (x !== null) rec[f] = x;
29+ }
30+ return rec;
31+ }
32+ return null;
33+ }
3434
35− function toJS(v) {
36− var type = typeof v;
37− if (type === 'number' || type === 'boolean' || type === 'string') return v;
38− if (type === 'object' && '_' in v) {
39− var obj = {};
40− for (var k in v) {
41− var x = toJS(v[k]);
42− if (x !== null) obj[k] = x;
43− }
44− return obj;
45− }
46− if (type === 'object' && (v.ctor === '::' || v.ctor === '[]')) {
47− var array = List.toArray(v);
48− for (var i = array.length; i--; ) {
49− array[i] = toJS(array[i]);
50− }
51− return array;
52− }
53− return null;
54− }
35+ function toJS(v) {
36+ var type = typeof v;
37+ if (type === 'number' || type === 'boolean' || type === 'string') return v;
38+ if (type === 'object' && '_' in v) {
39+ var obj = {};
40+ for (var k in v) {
41+ var x = toJS(v[k]);
42+ if (x !== null) obj[k] = x;
43+ }
44+ return obj;
45+ }
46+ if (type === 'object' && (v.ctor === '::' || v.ctor === '[]')) {
47+ var array = List.toArray(v);
48+ for (var i = array.length; i--; ) {
49+ array[i] = toJS(array[i]);
50+ }
51+ return array;
52+ }
53+ return null;
54+ }
5555
56− function fromRecord(r) {
57− if (typeof r === 'object' && '_' in r) {
58− return toJS(r);
59− }
60− throw new Error("'fromRecord' must be called on a record.");
61− }
56+ function fromRecord(r) {
57+ if (typeof r === 'object' && '_' in r) {
58+ return toJS(r);
59+ }
60+ throw new Error("'fromRecord' must be called on a record.");
61+ }
6262
63− function id(n) { return n; }
64−
65− function toElement(w,h,domNode) {
66− return A3( newElement, w, h, {
67− ctor: 'Custom',
68− type: 'DomNode',
69− render: function(node) { return node; },
70− update: function(node,oldNode,newNode) {
71− if (node === newNode) return;
72− node.parentNode.replaceChild(newNode, node);
73− },
74− model: domNode
75− });
76− }
77−
78− function fromElement(element) {
79− return Render.render(element);
80− }
81−
82− return elm.Native.JavaScript.values = {
83− toInt : function(n) { return n|0; },
84− toFloat : function(n) { return +n; },
85− toBool : id,
86− toString : id,
87− toList : List.fromArray,
88− fromString : id,
89− fromList : List.toArray,
90− fromInt : id,
91− fromFloat : id,
92− fromBool : id,
93−
94− toElement : toElement,
95− fromElement : fromElement,
96− toRecord : fromJS,
97− fromRecord : fromRecord
98− };
63+ return elm.Native.JavaScript.values = {
64+ toRecord : fromJS,
65+ fromRecord : fromRecord
66+ };
9967
10068};
libraries/Native/Json.js+71−58
@@ -1,72 +1,85 @@
11Elm.Native.Json = {};
22Elm.Native.Json.make = function(elm) {
33
4− elm.Native = elm.Native || {};
5− elm.Native.Json = elm.Native.Json || {};
6− if (elm.Native.Json.values) return elm.Native.Json.values;
4+ elm.Native = elm.Native || {};
5+ elm.Native.Json = elm.Native.Json || {};
6+ if (elm.Native.Json.values) return elm.Native.Json.values;
77
8− var Maybe = Elm.Maybe.make(elm);
9− var Dict = Elm.Dict.make(elm);
10− var List = Elm.List.make(elm);
11− var JS = Elm.JavaScript.make(elm);
12− var Utils = Elm.Native.Utils.make(elm);
8+ var Maybe = Elm.Maybe.make(elm);
9+ var Dict = Elm.Dict.make(elm);
10+ var List = Elm.Native.List.make(elm);
11+ var Utils = Elm.Native.Utils.make(elm);
1312
14− function fromValue(v) {
15− switch (v.ctor) {
16− case 'Null' : return null;
17− case 'String' : return JS.fromString(v._0);
18− case 'Object' :
19− var obj = {};
20− var array = JS.fromList(Dict.toList(v._0));
21− for (var i = array.length; i--; ) {
22− obj[JS.fromString(array[i]._0)] = fromValue(array[i]._1);
23− }
24− return obj;
25− case 'Array' :
26− var array = JS.fromList(v._0);
27− for (var i = array.length; i--; ) {
28− array[i] = fromValue(array[i]);
29− }
30− return array;
31− default :
32− return v._0;
13+ function toJS(v) {
14+ switch (v.ctor) {
15+ case 'Null' : return null;
16+ case 'String' : return v._0;
17+ case 'Number' : return v._0;
18+ case 'Boolean': return v._0;
19+ case 'Object' :
20+ var obj = {};
21+ var array = List.toArray(Dict.toList(v._0));
22+ for (var i = array.length; i--; ) {
23+ var entry = array[i];
24+ obj[entry._0] = toJS(entry._1);
25+ }
26+ return obj;
27+ case 'Array' :
28+ var array = List.toArray(v._0);
29+ for (var i = array.length; i--; ) {
30+ array[i] = toJS(array[i]);
31+ }
32+ return array;
33+ }
3334 }
34− }
3535
36− function toPrettyJSString(sep, obj) {
37− return JSON.stringify(fromValue(obj), null, JS.fromString(sep));
38− }
36+ function toString(sep, value) {
37+ return JSON.stringify(toJS(value), null, sep);
38+ }
3939
40− function toValue(v) {
41− switch (typeof v) {
42− case 'string' : return { ctor:"String", _0: JS.toString(v) };
43− case 'number' : return { ctor:"Number", _0: JS.toFloat(v) };
44− case 'boolean': return { ctor:"Boolean" , _0: JS.toBool(v) };
45− case 'object' :
46− if (v === null) return { ctor:"Null" };
47− if (v instanceof Array) {
48− for (var i = v.length; i--; ) { v[i] = toValue(v[i]); }
49− return { ctor:"Array", _0: JS.toList(v) };
50− }
51− var array = [];
52− for (var k in v) array.push(Utils.Tuple2(JS.toString(k), toValue(v[k])));
53− return { ctor:"Object", _0: Dict.fromList(JS.toList(array)) };
40+ function fromJS(v) {
41+ switch (typeof v) {
42+ case 'string' : return { ctor:"String" , _0: v };
43+ case 'number' : return { ctor:"Number" , _0: v };
44+ case 'boolean': return { ctor:"Boolean", _0: v };
45+ case 'object' :
46+ if (v === null) return { ctor:"Null" };
47+ if (v instanceof Array) {
48+ var array = new Array(v.length);
49+ for (var i = v.length; i--; ) {
50+ array[i] = fromJS(v[i]);
51+ }
52+ return {
53+ ctor:"Array",
54+ _0: List.fromArray(array)
55+ };
56+ }
57+ var array = [];
58+ for (var key in v) {
59+ var value = fromJS(v[key]);
60+ array.push(Utils.Tuple2(key, value));
61+ }
62+ var list = List.fromArray(array);
63+ return {
64+ ctor:"Object",
65+ _0: Dict.fromList(list)
66+ };
67+ }
5468 }
55− }
5669
57− function fromJSString(str) {
58− try {
59− return Maybe.Just(toValue(JSON.parse(str)));
60− } catch (e) {
61− return Maybe.Nothing;
70+ function fromString(str) {
71+ try {
72+ return Maybe.Just(fromJS(JSON.parse(str)));
73+ } catch (e) {
74+ return Maybe.Nothing;
75+ }
6276 }
63− }
6477
65− return elm.Native.Json.values = {
66− toJSString : F2(toPrettyJSString),
67− fromJSString : fromJSString,
68− toJSObject : fromValue,
69− fromJSObject : toValue
70− };
78+ return elm.Native.Json.values = {
79+ toString : F2(toString),
80+ fromString : fromString,
81+ fromJS : fromJS,
82+ toJS : toJS,
83+ };
7184
7285};
libraries/Native/Regex.js+14−14
@@ -6,8 +6,8 @@ Elm.Native.Regex.make = function(elm) {
66 if ('values' in Elm.Native.Regex)
77 return elm.Native.Regex.values = Elm.Native.Regex.values;
88
9+ var List = Elm.Native.List.make(elm);
910 var Maybe = Elm.Maybe.make(elm);
10− var JS = Elm.JavaScript.make(elm);
1111
1212 function escape(str) {
1313 return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
@@ -20,14 +20,14 @@ Elm.Native.Regex.make = function(elm) {
2020 }
2121
2222 function contains(re, string) {
23− return JS.fromString(string).match(re) !== null;
23+ return string.match(re) !== null;
2424 }
2525
2626 function find(n, re, str) {
2727 n = n.ctor === "All" ? Infinity : n._0;
2828 var out = [];
2929 var number = 0;
30− var string = JS.fromString(str);
30+ var string = str;
3131 var result;
3232 while (number++ < n && (result = re.exec(string))) {
3333 var i = result.length - 1;
@@ -36,17 +36,17 @@ Elm.Native.Regex.make = function(elm) {
3636 var submatch = result[i];
3737 subs[--i] = submatch === undefined
3838 ? Maybe.Nothing
39− : Maybe.Just(JS.toString(submatch));
39+ : Maybe.Just(submatch);
4040 }
4141 out.push({
4242 _:{},
43− match: JS.toString(result[0]),
44− submatches: JS.toList(subs),
43+ match: result[0],
44+ submatches: List.fromArray(subs),
4545 index: result.index,
4646 number: number,
4747 });
4848 }
49− return JS.toList(out);
49+ return List.fromArray(out);
5050 }
5151
5252 function replace(n, re, replacer, string) {
@@ -60,24 +60,24 @@ Elm.Native.Regex.make = function(elm) {
6060 var submatch = arguments[i];
6161 submatches[--i] = submatch === undefined
6262 ? Maybe.Nothing
63− : Maybe.Just(JS.toString(submatch));
63+ : Maybe.Just(submatch);
6464 }
65− return JS.fromString(replacer({
65+ return replacer({
6666 _:{},
6767 match:match,
68− submatches:JS.toList(submatches),
68+ submatches:List.fromArray(submatches),
6969 index:arguments[i-1],
7070 number:count
71− }));
71+ });
7272 }
7373 return string.replace(re, jsReplacer);
7474 }
7575
7676 function split(n, re, str) {
7777 if (n === Infinity) {
78− return JS.toList(JS.fromString(string).split(re));
78+ return List.fromArray(string.split(re));
7979 }
80− var string = JS.fromString(str);
80+ var string = str;
8181 var result;
8282 var out = [];
8383 var start = re.lastIndex;
@@ -87,7 +87,7 @@ Elm.Native.Regex.make = function(elm) {
8787 start = re.lastIndex;
8888 }
8989 out.push(string.slice(start));
90− return JS.toList(out);
90+ return List.fromArray(out);
9191 }
9292
9393 return Elm.Native.Regex.values = {
libraries/Native/Show.js+4
@@ -5,6 +5,7 @@ Elm.Native.Show.make = function(elm) {
55 if (elm.Native.Show.values) return elm.Native.Show.values;
66
77 var NList = Elm.Native.List.make(elm);
8+ var Array = Elm.Array.make(elm);
89 var List = Elm.List.make(elm);
910 var Dict = Elm.Dict.make(elm);
1011 var Tuple2 = Elm.Native.Utils.make(elm).Tuple2;
@@ -43,6 +44,9 @@ Elm.Native.Show.make = function(elm) {
4344 output.push(toString(v[k]));
4445 }
4546 return "(" + output.join(",") + ")";
47+ } else if (v.ctor === "_Array") {
48+ var list = Array.toList(v);
49+ return "Array.fromList " + toString(list);
4650 } else if (v.ctor === "::") {
4751 var output = '[' + toString(v._0);
4852 v = v._1;
libraries/Native/Signal/Http.js+6−7
@@ -5,7 +5,6 @@ Elm.Native.Http.make = function(elm) {
55 elm.Native.Http = elm.Native.Http || {};
66 if (elm.Native.Http.values) return elm.Native.Http.values;
77
8− var JS = Elm.JavaScript.make(elm);
98 var List = Elm.List.make(elm);
109 var Signal = Elm.Signal.make(elm);
1110
@@ -38,17 +37,17 @@ Elm.Native.Http.make = function(elm) {
3837 request.onreadystatechange = function(e) {
3938 if (request.readyState === 4) {
4039 response.value = (request.status >= 200 && request.status < 300 ?
41− { ctor:'Success', _0:JS.toString(request.responseText) } :
42− { ctor:'Failure', _0:request.status, _1:JS.toString(request.statusText) });
40+ { ctor:'Success', _0:request.responseText } :
41+ { ctor:'Failure', _0:request.status, _1:request.statusText });
4342 setTimeout(function() { updateQueue(queue,responses); }, 0);
4443 }
4544 };
46− request.open(JS.fromString(req.verb), JS.fromString(req.url), true);
45+ request.open(req.verb, req.url, true);
4746 function setHeader(pair) {
48− request.setRequestHeader( JS.fromString(pair._0), JS.fromString(pair._1) );
47+ request.setRequestHeader( pair._0, pair._1 );
4948 }
50− List.map(setHeader)(req.headers);
51− request.send(JS.fromString(req.body));
49+ A2( List.map, setHeader, req.headers );
50+ request.send(req.body);
5251 }
5352
5453 function send(requests) {
libraries/Native/Signal/Touch.js+3−3
@@ -6,7 +6,7 @@ Elm.Native.Touch.make = function(elm) {
66 if (elm.Native.Touch.values) return elm.Native.Touch.values;
77
88 var Signal = Elm.Signal.make(elm);
9− var JS = Elm.JavaScript.make(elm);
9+ var List = Elm.Native.List.make(elm);
1010 var Utils = Elm.Native.Utils.make(elm);
1111
1212 function Dict() {
@@ -136,7 +136,7 @@ Elm.Native.Touch.make = function(elm) {
136136 return sig;
137137 }
138138
139− var touches = dependency(JS.toList);
139+ var touches = dependency(List.fromArray);
140140
141141 var taps = function() {
142142 var sig = dependency(function(_) { return tap; });
@@ -149,4 +149,4 @@ Elm.Native.Touch.make = function(elm) {
149149
150150 return elm.Native.Touch.values = { touches: touches, taps: taps };
151151
152−};
152+};
libraries/Native/Signal/WebSocket.js+3−5
@@ -6,12 +6,11 @@ Elm.Native.WebSocket.make = function(elm) {
66 if (elm.Native.WebSocket.values) return elm.Native.WebSocket.values;
77
88 var Signal = Elm.Signal.make(elm);
9− var JS = Elm.JavaScript.make(elm);
109 var List = Elm.Native.List.make(elm);
1110
1211 function open(url, outgoing) {
1312 var incoming = Signal.constant(List.Nil);
14− var ws = new WebSocket(JS.fromString(url));
13+ var ws = new WebSocket(url);
1514
1615 var pending = [];
1716 var ready = false;
@@ -22,12 +21,11 @@ Elm.Native.WebSocket.make = function(elm) {
2221 ready = true;
2322 };
2423 ws.onmessage = function(event) {
25− elm.notify(incoming.id, JS.toString(event.data));
24+ elm.notify(incoming.id, event.data);
2625 };
2726
2827 function send(msg) {
29− var s = JS.fromString(msg);
30− ready ? ws.send(s) : pending.push(s);
28+ ready ? ws.send(msg) : pending.push(msg);
3129 }
3230
3331 function take1(x,y) { return x }
libraries/Native/String.js+9−9
@@ -8,7 +8,7 @@ Elm.Native.String.make = function(elm) {
88
99 var Char = Elm.Char.make(elm);
1010 var Maybe = Elm.Maybe.make(elm);
11− var JS = Elm.JavaScript.make(elm);
11+ var List = Elm.Native.List.make(elm);
1212 var Utils = Elm.Native.Utils.make(elm);
1313
1414 function isEmpty(str) {
@@ -26,7 +26,7 @@ Elm.Native.String.make = function(elm) {
2626 return a + b;
2727 }
2828 function concat(strs) {
29− return JS.fromList(strs).join('');
29+ return List.toArray(strs).join('');
3030 }
3131 function length(str) {
3232 return str.length;
@@ -59,10 +59,10 @@ Elm.Native.String.make = function(elm) {
5959 }
6060
6161 function split(sep, str) {
62− return JS.toList(str.split(sep));
62+ return List.fromArray(str.split(sep));
6363 }
6464 function join(sep, strs) {
65− return JS.fromList(strs).join(sep);
65+ return List.toArray(strs).join(sep);
6666 }
6767 function repeat(n, str) {
6868 var result = '';
@@ -111,10 +111,10 @@ Elm.Native.String.make = function(elm) {
111111 }
112112
113113 function words(str) {
114− return JS.toList(str.split(/\s+/g));
114+ return List.fromArray(str.split(/\s+/g));
115115 }
116116 function lines(str) {
117− return JS.toList(str.split(/\r\n|\r|\n/g));
117+ return List.fromArray(str.split(/\r\n|\r|\n/g));
118118 }
119119
120120 function toUpper(str) {
@@ -155,7 +155,7 @@ Elm.Native.String.make = function(elm) {
155155 is.push(i);
156156 i = i + subLen;
157157 }
158− return JS.toList(is);
158+ return List.fromArray(is);
159159 }
160160
161161 function toInt(s) {
@@ -193,10 +193,10 @@ Elm.Native.String.make = function(elm) {
193193 }
194194
195195 function toList(str) {
196− return JS.toList(str.split('').map(Utils.chr));
196+ return List.fromArray(str.split('').map(Utils.chr));
197197 }
198198 function fromList(chars) {
199− return JS.fromList(chars).join('');
199+ return List.toArray(chars).join('');
200200 }
201201
202202 return Elm.Native.String.values = {
libraries/Native/Text.js+1
@@ -153,6 +153,7 @@ Elm.Native.Text.make = function(elm) {
153153 typeface : F2(typeface),
154154 color : F2(color),
155155 link : F2(link),
156+ style : F2(style),
156157
157158 leftAligned : block('left'),
158159 rightAligned : block('right'),
libraries/Native/Utils.js+39−22
@@ -5,23 +5,30 @@ Elm.Native.Utils.make = function(elm) {
55 elm.Native.Utils = elm.Native.Utils || {};
66 if (elm.Native.Utils.values) return elm.Native.Utils.values;
77
8− function eq(x,y) {
9− if (x === y) return true;
10− if (typeof x === "object") {
11− var c = 0;
12− for (var i in x) {
13− ++c;
14− if (!eq(x[i],y[i])) {
15− return false;
8+ function eq(l,r) {
9+ var stack = [{'x': l, 'y': r}]
10+ while (stack.length > 0) {
11+ var front = stack.pop();
12+ var x = front.x;
13+ var y = front.y;
14+ if (x === y) continue;
15+ if (typeof x === "object") {
16+ var c = 0;
17+ for (var i in x) {
18+ ++c;
19+ stack.push({ 'x': x[i], 'y': y[i] });
1620 }
21+ if (c !== Object.keys(y).length) {
22+ return false;
23+ };
24+ } else if (typeof x === 'function') {
25+ throw new Error('Equality error: general function equality is ' +
26+ 'undecidable, and therefore, unsupported');
27+ } else {
28+ return false;
1729 }
18− return c === Object.keys(y).length;
1930 }
20− if (typeof x === 'function') {
21− throw new Error('Equality error: general function equality is ' +
22− 'undecidable, and therefore, unsupported');
23− }
24− return x === y;
31+ return true;
2532 }
2633
2734 // code in Generate/JavaScript.hs depends on the particular
@@ -30,11 +37,15 @@ Elm.Native.Utils.make = function(elm) {
3037 function compare(x,y) { return { ctor: ord[cmp(x,y)+1] } }
3138 function cmp(x,y) {
3239 var ord;
33− if (typeof x !== 'object' || x instanceof String){
40+ if (typeof x !== 'object'){
3441 return x === y ? EQ : x < y ? LT : GT;
3542 }
36−
37− if (x.ctor === "::" || x.ctor === "[]") {
43+ else if (x.isChar){
44+ var a = x.toString();
45+ var b = y.toString();
46+ return a === b ? EQ : a < b ? LT : GT;
47+ }
48+ else if (x.ctor === "::" || x.ctor === "[]") {
3849 while (true) {
3950 if (x.ctor === "[]" && y.ctor === "[]") return EQ;
4051 if (x.ctor !== y.ctor) return x.ctor === '[]' ? LT : GT;
@@ -44,8 +55,7 @@ Elm.Native.Utils.make = function(elm) {
4455 y = y._1;
4556 }
4657 }
47−
48− if (x.ctor.slice(0,6) === '_Tuple') {
58+ else if (x.ctor.slice(0,6) === '_Tuple') {
4959 var n = x.ctor.slice(6) - 0;
5060 var err = 'cannot compare tuples with more than 6 elements.';
5161 if (n === 0) return EQ;
@@ -58,9 +68,11 @@ Elm.Native.Utils.make = function(elm) {
5868 if (n >= 7) throw new Error('Comparison error: ' + err); } } } } } }
5969 return EQ;
6070 }
61− throw new Error('Comparison error: comparison is only defined on ints, ' +
62− 'floats, times, chars, strings, lists of comparable values, ' +
63− 'and tuples of comparable values.')
71+ else {
72+ throw new Error('Comparison error: comparison is only defined on ints, ' +
73+ 'floats, times, chars, strings, lists of comparable values, ' +
74+ 'and tuples of comparable values.');
75+ }
6476 }
6577
6678
@@ -205,6 +217,10 @@ Elm.Native.Utils.make = function(elm) {
205217 return Tuple2(posx, posy);
206218 }
207219
220+ function isJSArray(a) {
221+ return a instanceof Array;
222+ }
223+
208224 return elm.Native.Utils.values = {
209225 eq:eq,
210226 cmp:cmp,
@@ -224,6 +240,7 @@ Elm.Native.Utils.make = function(elm) {
224240 mod : F2(mod),
225241 htmlHeight: F2(htmlHeight),
226242 getXY: getXY,
243+ isJSArray: isJSArray,
227244 toFloat: function(x) { return +x; }
228245 };
229246};
libraries/Set.elm+11−1
@@ -2,6 +2,7 @@
22module Set (empty,singleton,insert,remove
33 ,member
44 ,foldl,foldr,map
5+ ,filter,partition
56 ,union,intersect,diff
67 ,toList,fromList
78 ) where
@@ -25,7 +26,7 @@ Insert, remove, and query operations all take *O(log n)* time.
2526@docs toList, fromList
2627
2728# Transform
28−@docs map, foldl, foldr
29+@docs map, foldl, foldr, filter, partition
2930
3031-}
3132
@@ -88,3 +89,12 @@ foldr f b s = Dict.foldr (\k _ b -> f k b) b s
8889{-| Map a function onto a set, creating a new set with no duplicates. -}
8990map : (comparable -> comparable') -> Set comparable -> Set comparable'
9091map f s = fromList (List.map f (toList s))
92+
93+{-| Create a new set consisting only of elements which satisfy a predicate. -}
94+filter : (comparable -> Bool) -> Set comparable -> Set comparable
95+filter p set = Dict.filter (\k _ -> p k) set
96+
97+{-| Create two new sets; the first consisting of elements which satisfy a
98+predicate, the second consisting of elements which do not. -}
99+partition : (comparable -> Bool) -> Set comparable -> (Set comparable, Set comparable)
100+partition p set = Dict.partition (\k _ -> p k) set
libraries/Signal.elm+8−13
@@ -31,10 +31,7 @@ the `Time` library.
3131
3232import Native.Signal
3333import List (foldr)
34−
3534import Basics (fst, snd, not)
36−import Native.Error
37−import Maybe as M
3835
3936data Signal a = Signal
4037
@@ -120,20 +117,18 @@ initially. -}
120117dropIf : (a -> Bool) -> a -> Signal a -> Signal a
121118dropIf = Native.Signal.dropIf
122119
123−{-| Keep events only when the first signal is true. When the first signal
124−becomes true, the most recent value of the second signal will be propagated.
125−Until the first signal becomes false again, all events will be propagated. Elm
126−does not allow undefined signals, so a base case must be provided in case the
127−first signal is not true initially. -}
120+{-| Keep events only when the first signal is true. Elm does not allow undefined
121+signals, so a base case must be provided in case the first signal is not true
122+initially.
123+-}
128124keepWhen : Signal Bool -> a -> Signal a -> Signal a
129125keepWhen bs def sig =
130126 snd <~ (keepIf fst (False, def) ((,) <~ (sampleOn sig bs) ~ sig))
131127
132−{-| Drop events when the first signal is true. When the first signal becomes
133−false, the most recent value of the second signal will be propagated. Until the
134−first signal becomes true again, all events will be propagated. Elm does not
135−allow undefined signals, s oa base case must be provided in case the first
136−signal is true initially. -}
128+{-| Drop events when the first signal is true. Elm does not allow undefined
129+signals, so a base case must be provided in case the first signal is true
130+initially.
131+-}
137132dropWhen : Signal Bool -> a -> Signal a -> Signal a
138133dropWhen bs = keepWhen (not <~ bs)
139134
libraries/String.elm+1−1
@@ -114,7 +114,7 @@ foldr = Native.String.foldr
114114{-| Split a string using a given separator.
115115
116116 split "," "cat,dog,cow" == ["cat","dog","cow"]
117− split "/" "home/evan/Desktop/" == ["home","evan","Desktop"]
117+ split "/" "home/evan/Desktop/" == ["home","evan","Desktop", ""]
118118
119119Use `Regex.split` if you need something more flexible.
120120-}
libraries/Text.elm−1
@@ -35,7 +35,6 @@ import String
3535import Color (Color, black)
3636import Graphics.Element (Element, Three, Pos, ElementPrim, Properties)
3737import Maybe (Maybe, Nothing)
38−import JavaScript (JSString)
3938import Native.Show
4039import Native.Text
4140
libraries/elm_dependencies.json+6−6
@@ -1,12 +1,13 @@
1−{ "version": "0.12"
1+{ "version": "0.12.1"
22, "summary": "Elm's standard libraries"
3−, "description": "The full set of standard libraries for Elm. This library is pegged to the version number of the compiler, so if you are using Elm 0.12, you should be using version 0.12 of the standard libraries."
3+, "description": "The full set of standard libraries for Elm. This library is pegged to the version number of the compiler, so if you are using Elm 0.12.1, you should be using version 0.12.1 of the standard libraries."
44, "license": "BSD3"
5−, "repository": "http://github.com/evancz/Elm.git"
6−, "elm-version": "0.12"
5+, "repository": "http://github.com/elm-lang/Elm.git"
6+, "elm-version": "0.12.1"
77, "dependencies": {}
88, "exposed-modules":
9− [ "Basics"
9+ [ "Array"
10+ , "Basics"
1011 , "Bitwise"
1112 , "Char"
1213 , "Color"
@@ -19,7 +20,6 @@
1920 , "Graphics.Input"
2021 , "Graphics.Input.Field"
2122 , "Http"
22− , "JavaScript"
2323 , "JavaScript.Experimental"
2424 , "Json"
2525 , "Keyboard"
runtime/Render/Element.js+8−2
@@ -31,6 +31,7 @@ function setProps(elem, e) {
3131 a.style.display = 'block';
3232 a.style.position = 'absolute';
3333 e.style.position = 'relative';
34+ a.style.pointerEvents = 'auto';
3435 e.appendChild(a);
3536 }
3637 if (props.hover.ctor !== '_Tuple0') {
@@ -45,8 +46,9 @@ function setProps(elem, e) {
4546function addClick(e, handler) {
4647 e.style.pointerEvents = 'auto';
4748 e.elm_click_handler = handler;
48− function trigger() {
49+ function trigger(ev) {
4950 e.elm_click_handler(Utils.Tuple0);
51+ ev.stopPropagation();
5052 }
5153 e.elm_click_trigger = trigger;
5254 e.addEventListener('click', trigger);
@@ -63,14 +65,16 @@ function addHover(e, handler) {
6365 e.elm_hover_handler = handler;
6466 e.elm_hover_count = 0;
6567
66− function over() {
68+ function over(evt) {
6769 if (e.elm_hover_count++ > 0) return;
6870 e.elm_hover_handler(true);
71+ evt.stopPropagation();
6972 }
7073 function out(evt) {
7174 if (e.contains(evt.toElement || evt.relatedTarget)) return;
7275 e.elm_hover_count = 0;
7376 e.elm_hover_handler(false);
77+ evt.stopPropagation();
7478 }
7579 e.elm_hover_over = over;
7680 e.elm_hover_out = out;
@@ -232,6 +236,7 @@ function rawHtml(elem) {
232236 }
233237 document.body.removeChild(div);
234238 div.style.visibility = 'visible';
239+ div.style.pointerEvents = 'auto';
235240 return div;
236241}
237242
@@ -375,6 +380,7 @@ function updateProps(node, curr, next) {
375380 a.style.display = 'block';
376381 a.style.position = 'absolute';
377382 e.style.position = 'relative';
383+ a.style.pointerEvents = 'auto';
378384 e.appendChild(a);
379385 } else {
380386 node.lastNode.href = props.href;
server/LICENSEdeleted−30
@@ -1,30 +0,0 @@
1−Copyright (c)2012-2014, Evan Czaplicki
2−
3−All rights reserved.
4−
5−Redistribution and use in source and binary forms, with or without
6−modification, are permitted provided that the following conditions are met:
7−
8− * Redistributions of source code must retain the above copyright
9− notice, this list of conditions and the following disclaimer.
10−
11− * Redistributions in binary form must reproduce the above
12− copyright notice, this list of conditions and the following
13− disclaimer in the documentation and/or other materials provided
14− with the distribution.
15−
16− * Neither the name of Evan Czaplicki nor the names of other
17− contributors may be used to endorse or promote products derived
18− from this software without specific prior written permission.
19−
20−THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21−"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22−LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23−A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24−OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25−SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26−LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27−DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28−THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29−(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30−OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
server/Server.hsdeleted−127
@@ -1,127 +0,0 @@
1−{-# OPTIONS_GHC -Wall #-}
2−{-# LANGUAGE OverloadedStrings, DeriveDataTypeable #-}
3−module Main where
4−
5−import Control.Applicative ((<$>),(<|>))
6−import Control.Monad (guard)
7−import Control.Monad.Trans (MonadIO(liftIO))
8−import qualified Data.ByteString as BS
9−import qualified Data.ByteString.Char8 as BSC
10−import qualified Data.HashMap.Strict as Map
11−import qualified Data.Version as Version
12−import System.Console.CmdArgs
13−import System.Directory
14−import System.Exit
15−import System.FilePath
16−import System.Process
17−import System.IO (hGetContents, Handle)
18−
19−import Paths_elm_server (version)
20−import qualified Elm.Internal.Paths as Elm
21−import Snap.Core
22−import Snap.Http.Server
23−import Snap.Util.FileServe
24−
25−data Flags = Flags
26− { port :: Int
27− , runtime :: Maybe FilePath
28− } deriving (Data,Typeable,Show,Eq)
29−
30−flags :: Flags
31−flags = Flags
32− { port = 8000 &= help "set the port of the server"
33− , runtime = Nothing &= typFile
34− &= help "Specify a custom location for Elm's runtime system."
35− } &= help "Quickly reload Elm projects in your browser. Just refresh to recompile.\n\
36− \It serves static files and freshly recompiled Elm files."
37− &= helpArg [explicit, name "help", name "h"]
38− &= versionArg [ explicit, name "version", name "v"
39− , summary (Version.showVersion version)
40− ]
41− &= summary ("Elm Server " ++ Version.showVersion version ++
42− ", (c) Evan Czaplicki 2011-2014")
43−
44−
45−config :: Config Snap a
46−config = setAccessLog ConfigNoLog (setErrorLog ConfigNoLog defaultConfig)
47−
48−-- | Set up the server.
49−main :: IO ()
50−main = do
51− cargs <- cmdArgs flags
52− putStrLn $ "Elm Server " ++ Version.showVersion version ++
53− ": Just refresh a page to recompile it!"
54− httpServe (setPort (port cargs) config) $
55− serveRuntime (maybe Elm.runtime id (runtime cargs))
56− <|> serveElm
57− <|> serveDirectoryWith directoryConfig "."
58−
59−directoryConfig :: MonadSnap m => DirectoryConfig m
60−directoryConfig =
61− fancyDirectoryConfig
62− { indexGenerator = defaultIndexGenerator defaultMimeTypes indexStyle
63− , mimeTypes = Map.insert ".elm" "text/html" defaultMimeTypes
64− }
65−
66−indexStyle :: BS.ByteString
67−indexStyle =
68− "body { margin:0; font-family:sans-serif; background:rgb(245,245,245);\
69− \ font-family: calibri, verdana, helvetica, arial; }\
70− \div.header { padding: 40px 50px; font-size: 24px; }\
71− \div.content { padding: 0 40px }\
72− \div.footer { display:none; }\
73− \table { width:100%; border-collapse:collapse; }\
74− \td { padding: 6px 10px; }\
75− \tr:nth-child(odd) { background:rgb(216,221,225); }\
76− \td { font-family:monospace }\
77− \th { background:rgb(90,99,120); color:white; text-align:left;\
78− \ padding:10px; font-weight:normal; }"
79−
80−runtimeName :: String
81−runtimeName = "elm-runtime.js"
82−
83−serveRuntime :: FilePath -> Snap ()
84−serveRuntime runtimePath =
85− do file <- BSC.unpack . rqPathInfo <$> getRequest
86− guard (file == runtimeName)
87− serveFileAs "application/javascript" runtimePath
88−
89−serveElm :: Snap ()
90−serveElm =
91− do file <- BSC.unpack . rqPathInfo <$> getRequest
92− exists <- liftIO $ doesFileExist file
93− guard (exists && takeExtension file == ".elm")
94− onSuccess (compile file) (serve file)
95− where
96− compile file =
97− let elmArgs = [ "--make", "--runtime=" ++ runtimeName, file ]
98− in createProcess $ (proc "elm" elmArgs) { std_out = CreatePipe }
99−
100− serve file =
101− serveFileAs "text/html; charset=UTF-8" ("build" </> replaceExtension file "html")
102−
103−failure :: String -> Snap ()
104−failure msg =
105− do modifyResponse $ setResponseStatus 404 "Not found"
106− writeBS $ BSC.pack msg
107−
108−onSuccess :: IO (t, Maybe Handle, t1, ProcessHandle) -> Snap () -> Snap ()
109−onSuccess action success =
110− do (_, stdout, _, handle) <- liftIO action
111− exitCode <- liftIO $ waitForProcess handle
112− case (exitCode, stdout) of
113− (ExitFailure 127, _) ->
114− failure "Error: elm compiler not found in your path."
115−
116− (ExitFailure _, Just out) ->
117− failure =<< liftIO (hGetContents out)
118−
119− (ExitFailure _, Nothing) ->
120− failure "See command line for error message."
121−
122− (ExitSuccess, _) -> success
123−
124−{--
125−pageTitle :: String -> String
126−pageTitle = dropExtension . takeBaseName
127−--}
server/Setup.hsdeleted−2
@@ -1,2 +0,0 @@
1−import Distribution.Simple
2−main = defaultMain
server/elm-server.cabaldeleted−41
@@ -1,41 +0,0 @@
1−Name: elm-server
2−Version: 0.11
3−Synopsis: Server for developing Elm projects
4−Description: Provides a standalone Snap server that serves static files.
5− For Elm files, it recompiles them and serves them as HTML,
6− so you can just refresh to see the new version.
7−
8−Homepage: http://elm-lang.org
9−
10−License: BSD3
11−License-file: LICENSE
12−
13−Author: Evan Czaplicki
14−Maintainer: info@elm-lang.org
15−Copyright: Copyright: (c) 2011-2014 Evan Czaplicki
16−
17−Category: Compiler, Language
18−
19−Build-type: Simple
20−
21−--Extra-source-files: README.md
22−Cabal-version: >=1.8
23−
24−source-repository head
25− type: git
26− location: git://github.com/evancz/Elm.git
27−
28−Executable elm-server
29− Main-is: Server.hs
30− Build-depends: base >=4.2 && <5,
31− bytestring,
32− cmdargs,
33− containers >= 0.3,
34− directory,
35− filepath,
36− Elm >= 0.11,
37− snap-core,
38− snap-server,
39− mtl,
40− process,
41− unordered-containers
tests/elm/Test.elmnew file+37
@@ -0,0 +1,37 @@
1+module Main where
2+
3+import ElmTest.Assertion as A
4+import ElmTest.Run as R
5+import ElmTest.Runner.Console (runDisplay)
6+import ElmTest.Test (..)
7+
8+import IO.IO (..)
9+import IO.Runner (Request, Response)
10+import IO.Runner as Run
11+
12+import Test.Array as Array
13+import Test.Dict as Dict
14+import Test.List as List
15+import Test.Set as Set
16+import Test.String as String
17+import Test.Trampoline as Trampoline
18+
19+tests : Test
20+tests = suite "Elm Standard Library Tests" [ List.tests,
21+ String.tests,
22+ Trampoline.tests,
23+ Array.tests,
24+ Dict.tests,
25+ Set.tests
26+ ]
27+
28+console : IO ()
29+console = runDisplay tests
30+
31+port requests : Signal [{ mPut : Maybe String
32+ , mExit : Maybe Int
33+ , mGet : Bool
34+ }]
35+port requests = Run.run responses console
36+
37+port responses : Signal (Maybe String)
tests/elm/Test/Array.elmnew file+54
@@ -0,0 +1,54 @@
1+module Test.Array (tests) where
2+
3+import List
4+import Array
5+
6+import ElmTest.Assertion (..)
7+import ElmTest.Test (..)
8+
9+tests : Test
10+tests =
11+ let creationTests = suite "Creation"
12+ [ test "empty" <| assertEqual Array.empty (Array.fromList [])
13+ , test "initialize" <| assertEqual (Array.initialize 4 id) (Array.fromList [0,1,2,3])
14+ , test "initialize 2" <| assertEqual (Array.initialize 4 (\n -> n*n)) (Array.fromList [0,1,4,9])
15+ , test "initialize 3" <| assertEqual (Array.initialize 4 (always 0)) (Array.fromList [0,0,0,0])
16+ , test "initialize Empty" <| assertEqual (Array.initialize 0 id) Array.empty
17+ , test "initialize 4" <| assertEqual (Array.initialize 2 (always 0)) (Array.fromList [0,0])
18+ , test "repeat" <| assertEqual (Array.repeat 5 40) (Array.fromList [40,40,40,40,40])
19+ , test "repeat 2" <| assertEqual (Array.repeat 5 0) (Array.fromList [0,0,0,0,0])
20+ , test "repeat 3" <| assertEqual (Array.repeat 3 "cat") (Array.fromList ["cat","cat","cat"])
21+ , test "fromList" <| assertEqual (Array.fromList []) Array.empty
22+ ]
23+ basicsTests = suite "Basics"
24+ [ test "length" <| assertEqual (Array.length (Array.fromList [1,2,3])) 3
25+ , test "length - Long" <| assertEqual (Array.length (Array.repeat 10000 0)) 10000
26+ , test "push" <| assertEqual (Array.push 3 (Array.fromList [1,2])) (Array.fromList [1,2,3])
27+ , test "append" <| assertEqual (Array.toList <| Array.append (Array.repeat 2 42) (Array.repeat 3 81)) ([42,42,81,81,81])
28+ ]
29+ getAndSetTests = suite "Get and Set"
30+ [ test "get" <| assertEqual (Array.get 1 (Array.fromList [3,2,1])) (Just 2)
31+ , test "get 2" <| assertEqual (Array.get 5 (Array.fromList [3,2,1])) Nothing
32+ , test "get 3" <| assertEqual (Array.get -1 (Array.fromList [3,2,1])) Nothing
33+ , test "getOrElse 1" <| assertEqual (Array.getOrElse 0 2 (Array.fromList [3,2,1])) 1
34+ , test "getOrElse 2" <| assertEqual (Array.getOrElse 0 5 (Array.fromList [3,2,1])) 0
35+ , test "getOrFail" <| assertEqual (Array.getOrFail 2 (Array.fromList [3,2,1])) 1
36+ , test "set" <| assertEqual (Array.set 1 7 (Array.fromList [1,2,3])) (Array.fromList [1,7,3])
37+ ]
38+ takingArraysApartTests = suite "Taking Arrays Apart"
39+ [ test "toList" <| assertEqual (Array.toList (Array.fromList [3,5,8])) [3,5,8]
40+ , test "toIndexedList" <| assertEqual (Array.toIndexedList (Array.fromList ["cat","dog"])) [(0,"cat"), (1,"dog")]
41+ , test "slice" <| assertEqual (Array.slice 1 2 (Array.fromList [0,1,2,3,4])) (Array.fromList [1,2])
42+ , test "slice 2" <| assertEqual (Array.slice 1 -2 (Array.fromList [0,1,2,3,4])) (Array.fromList [1,2,3])
43+ , test "slice 3" <| assertEqual (Array.slice -3 -2 (Array.fromList [0,1,2,3,4])) (Array.fromList [2,3])
44+ ]
45+ mappingAndFoldingTests = suite "Mapping and Folding"
46+ [ test "map" <| assertEqual (Array.map sqrt (Array.fromList [1,4,9])) (Array.fromList [1,2,3])
47+ , test "indexedMap" <| assertEqual (Array.indexedMap (*) (Array.fromList [5,5,5])) (Array.fromList [0,5,10])
48+ , test "foldl" <| assertEqual (Array.foldl (::) [] (Array.fromList [1,2,3])) [3,2,1]
49+ , test "foldr" <| assertEqual (Array.foldr (+) 0 (Array.repeat 3 5)) 15
50+ , test "foldr 2" <| assertEqual (Array.foldr (::) [] (Array.fromList [1,2,3])) [1,2,3]
51+ , test "filter" <| assertEqual (Array.filter (\x -> x `mod` 2 == 0) (Array.fromList [1..6])) (Array.fromList [2,4,6])
52+ ]
53+ in
54+ suite "Array Tests" [creationTests, basicsTests, getAndSetTests, takingArraysApartTests, mappingAndFoldingTests]
tests/elm/Test/Dict.elmnew file+26
@@ -0,0 +1,26 @@
1+module Test.Dict (tests) where
2+
3+import Dict
4+import List
5+
6+import ElmTest.Assertion (..)
7+import ElmTest.Test (..)
8+
9+animals : Dict.Dict String String
10+animals = Dict.fromList [ ("Tom", "cat"), ("Jerry", "mouse") ]
11+
12+tests : Test
13+tests =
14+ let getTests =
15+ [ test "get 1" <| assertEqual (Just "cat") (Dict.get "Tom" animals)
16+ , test "get 2" <| assertEqual Nothing (Dict.get "Spike" animals)
17+ , test "getOrElse 1" <| assertEqual "mouse" (Dict.getOrElse "dog" "Jerry" animals)
18+ , test "getOrElse 2" <| assertEqual "dog" (Dict.getOrElse "dog" "Spike" animals)
19+ , test "getOrFail" <| assertEqual "cat" (Dict.getOrFail "Tom" animals)
20+ ]
21+ filterTests =
22+ [ test "filter" <| assertEqual (Dict.singleton "Tom" "cat") (Dict.filter (\k v -> k == "Tom") animals)
23+ , test "partition" <| assertEqual (Dict.singleton "Tom" "cat", Dict.singleton "Jerry" "mouse") (Dict.partition (\k v -> k == "Tom") animals)
24+ ]
25+ in
26+ suite "Dict Tests" <| List.concat [ getTests, filterTests ]
tests/elm/Test/List.elmnew file+38
@@ -0,0 +1,38 @@
1+module Test.List (tests) where
2+
3+import List
4+
5+import ElmTest.Assertion (..)
6+import ElmTest.Test (..)
7+
8+tests : Test
9+tests =
10+ let partitionTests =
11+ let p1 = id
12+ p2 x = x < 3
13+ isEven n = n `mod` 2 == 0
14+ in suite "partition Tests" [ test "simple partition" <| assertEqual ([True],[False]) (List.partition p1 [False, True]),
15+ test "order check" <| assertEqual ([2,1], [5,6]) (List.partition p2 [2,5,6,1]),
16+ test "partition doc check 1" <| assertEqual ([0,1,2], [3,4,5]) (partition p2 [0..5]),
17+ test "partition doc check 2" <| assertEqual ([0,2,4], [1,3,5]) (partition isEven [0..5]),
18+ let n = 100000
19+ ts = repeat n True
20+ fs = repeat n False
21+ in test "partition stress test" <| assertEqual (ts, fs) (partition p1 (fs++ts))
22+ ]
23+ unzipTests = suite "unzip Tests" [
24+ test "unzip doc check" <| assertEqual ([0,17,1337],[True,False,True]) (unzip [(0, True), (17, False), (1337, True)]),
25+ let n = 100000
26+ ts = repeat n True
27+ fs = repeat n False
28+ in test "unzip stress test" <| assertEqual (ts, fs) (unzip (zip ts fs))
29+ ]
30+ intersperseTests = suite "intersperse Tests" [
31+ test "intersperse doc check" <| assertEqual ["turtles","on","turtles","on","turtles"] (intersperse "on" ["turtles","turtles","turtles"]),
32+ let n = 100000
33+ ts = repeat n True
34+ fs = repeat n False
35+ in test "intersperse stress test" <| assertEqual (tail . List.concat <| zipWith (\x y -> [x,y]) fs ts)
36+ (intersperse False ts)
37+ ]
38+ in suite "List Tests" [partitionTests, unzipTests, intersperseTests]
tests/elm/Test/Set.elmnew file+27
@@ -0,0 +1,27 @@
1+module Test.Set (tests) where
2+
3+import Set
4+import Set (Set)
5+import List
6+
7+import ElmTest.Assertion (..)
8+import ElmTest.Test (..)
9+
10+set : Set Int
11+set = Set.fromList [1..100]
12+
13+setPart1 : Set Int
14+setPart1 = Set.fromList [1..50]
15+
16+setPart2 : Set Int
17+setPart2 = Set.fromList [51..100]
18+
19+pred : Int -> Bool
20+pred x = x <= 50
21+
22+tests : Test
23+tests =
24+ let
25+ filterTests = suite "filter Tests" [test "Simple filter" <| assertEqual setPart1 <| Set.filter pred set]
26+ partitionTests = suite "partition Tests" [test "Simple partition" <| assertEqual (setPart1, setPart2) <| Set.partition pred set]
27+ in suite "Set Tests" [partitionTests, filterTests]
tests/elm/Test/String.elmnew file+34
@@ -0,0 +1,34 @@
1+module Test.String (tests) where
2+
3+import List
4+import String (..)
5+
6+import ElmTest.Assertion (..)
7+import ElmTest.Test (..)
8+
9+tests : Test
10+tests =
11+ let isEmptyTests = suite "isEmpty Tests" [
12+ test "empty is empty" <| assert <| isEmpty "",
13+ test "not empty" <| assert <| not . isEmpty <| "the world"
14+ ]
15+ lengthTest = test "length of innumerable" <| assertEqual 11 (length "innumerable")
16+ reverseTest = test "reverse test" <| assertEqual "desserts" (reverse "stressed")
17+ repeatTest = test "repeat test" <| assertEqual "hahaha" (repeat 3 "ha")
18+ unconsTests = suite "uncons Tests" [
19+ test "nonempty uncons" <| assertEqual (Just ('a',"bc")) (uncons "abc"),
20+ test "empty uncons" <| assertEqual Nothing (uncons "")
21+ ]
22+ appendTest = test "append test" <| assertEqual "butterfly" (append "butter" "fly")
23+ concatTest = test "concat test" <| assertEqual "nevertheless" (concat ["never","the","less"])
24+ splitTests = suite "split Tests" [
25+ test "split commas" <| assertEqual ["cat","dog","cow"] (split "," "cat,dog,cow"),
26+ test "split slashes"<| assertEqual ["home","evan","Desktop", ""] (split "/" "home/evan/Desktop/")
27+ ]
28+ joinTests = suite "join Tests" [
29+ test "join spaces" <| assertEqual "cat dog cow" (join " " ["cat","dog","cow"]),
30+ test "join slashes" <| assertEqual "home/evan/Desktop" (join "/" ["home","evan","Desktop"])
31+ ]
32+
33+ endsWithTest = test "endsWith Check" (assert <| endsWith "h" "th")
34+ in suite "String Tests" [isEmptyTests, lengthTest, reverseTest, repeatTest, unconsTests, appendTest, concatTest, splitTests, joinTests, endsWithTest]
tests/elm/Test/Trampoline.elmnew file+29
@@ -0,0 +1,29 @@
1+module Test.Trampoline (tests) where
2+
3+import Trampoline (..)
4+
5+import ElmTest.Assertion (..)
6+import ElmTest.Test (..)
7+
8+badSum : Int -> Int
9+badSum n =
10+ let loop x acc =
11+ case x of
12+ 0 -> acc
13+ _ -> loop (x-1) (acc+x)
14+ in loop n 0
15+
16+goodSum : Int -> Int
17+goodSum n =
18+ let sumT x acc =
19+ case x of
20+ 0 -> Done acc
21+ _ -> Continue (\_ -> sumT (x-1) (acc+x))
22+ in trampoline <| sumT n 0
23+
24+tests : Test
25+tests =
26+ let mkLoopCheck n = test ("Equivalent Loop Check #"++ show n) (assertEqual (badSum n) (goodSum n))
27+ loopChecks = suite "Equivalent to untrampolined Tests" <| map mkLoopCheck [0..25]
28+ noStackOverflow = test "Trampoline Deep Recursion Test" (assertEqual 500000500000 (goodSum 1000000))
29+ in suite "Trampoline Tests" [noStackOverflow, loopChecks]
tests/Main.hs → tests/hs/CompilerTest.hsrenamed

The content is the same.

tests/hs/LibTest.hsnew file+30
@@ -0,0 +1,30 @@
1+module Main where
2+
3+import Control.Monad (unless)
4+import Data.List (isInfixOf)
5+import System.Directory
6+import System.Exit
7+import System.FilePath
8+import System.Process
9+
10+import Elm.Internal.Paths as Elm
11+
12+main :: IO ()
13+main = do
14+ top <- getCurrentDirectory
15+ let ioScript s = top</>"IO"</>s
16+ runCmd "git submodule update --init"
17+ out <- readProcess "npm" ["ls", "--parseable"] ""
18+ unless ("jsdom" `isInfixOf` out) $ runCmd "npm install jsdom"
19+ setCurrentDirectory $ top</>"tests"</>"elm"
20+ runCmd $ concat [top</>"dist"</>"build"</>"elm"</>"elm --make --only-js --src-dir=" , top</>"automaton", " --src-dir=", top</>"IO", " --src-dir=", top</>"Elm-Test", " Test.elm"]
21+ runCmd $ unwords ["cat ", ioScript "prescript.js", Elm.runtime, "build"</>"Test.js", ioScript "handler.js", "> exe.js"]
22+ exitWith =<< waitForProcess =<< (runCommand "node exe.js")
23+ where runCmd cmd = do
24+ putStrLn cmd
25+ exitCode <- waitForProcess =<< runCommand cmd
26+ case exitCode of
27+ ExitSuccess -> return ()
28+ ExitFailure _ -> error "something went wrong"
29+
30+
tests/Tests/Compiler.hs → tests/hs/Tests/Compiler.hsrenamed

The content is the same.

tests/Tests/Property.hs → tests/hs/Tests/Property.hsrenamed

The content is the same.

tests/Tests/Property/Arbitrary.hs → tests/hs/Tests/Property/Arbitrary.hsrenamed

The content is the same.

tests/test-files/bad/BBTArgs → tests/hs/test-files/bad/BBTArgsrenamed

The content is the same.

tests/test-files/bad/InfiniteType.elm → tests/hs/test-files/bad/InfiniteType.elmrenamed

The content is the same.

tests/test-files/bad/NonElementMain.elm → tests/hs/test-files/bad/NonElementMain.elmrenamed

The content is the same.

tests/test-files/bad/Strings/ExtraClose.elm → tests/hs/test-files/bad/Strings/ExtraClose.elmrenamed

The content is the same.

tests/test-files/good/AliasSubstitution.elm → tests/hs/test-files/good/AliasSubstitution.elmrenamed

The content is the same.

tests/test-files/good/NoExpressions.elm → tests/hs/test-files/good/NoExpressions.elmrenamed

The content is the same.

tests/test-files/good/Otherwise.elm → tests/hs/test-files/good/Otherwise.elmrenamed

The content is the same.

tests/test-files/good/Ports.elm → tests/hs/test-files/good/Ports.elmrenamed

The content is the same.

tests/test-files/good/QuotesAndComments.elm → tests/hs/test-files/good/QuotesAndComments.elmrenamed

The content is the same.

tests/test-files/good/Soundness/Apply.elm → tests/hs/test-files/good/Soundness/Apply.elmrenamed

The content is the same.

tests/test-files/good/Soundness/ApplyAnnotated.elm → tests/hs/test-files/good/Soundness/ApplyAnnotated.elmrenamed

The content is the same.

tests/test-files/good/Soundness/Id.elm → tests/hs/test-files/good/Soundness/Id.elmrenamed

The content is the same.

tests/test-files/good/Soundness/IdAnnotated.elm → tests/hs/test-files/good/Soundness/IdAnnotated.elmrenamed

The content is the same.

tests/test-files/good/Soundness/TrickyId.elm → tests/hs/test-files/good/Soundness/TrickyId.elmrenamed

The content is the same.

tests/test-files/good/Soundness/TrickyIdAnnotated.elm → tests/hs/test-files/good/Soundness/TrickyIdAnnotated.elmrenamed

The content is the same.

tests/test-files/good/Strings/Multiline.elm → tests/hs/test-files/good/Strings/Multiline.elmrenamed

The content is the same.

tests/test-files/good/Strings/MultilineNormal.elm → tests/hs/test-files/good/Strings/MultilineNormal.elmrenamed

The content is the same.

tests/test-files/good/Unify/LockedVars.elm → tests/hs/test-files/good/Unify/LockedVars.elmrenamed

The content is the same.

tests/test-files/good/Unify/NonHomogeneousRecords.elm → tests/hs/test-files/good/Unify/NonHomogeneousRecords.elmrenamed

The content is the same.