Skip to content

Commit

Permalink
Merge pull request #1 from cobalamin/master
Browse files Browse the repository at this point in the history
Add `sequence` function
  • Loading branch information
Luke Westby authored Sep 11, 2016
2 parents e0feb2e + e3e3422 commit 1629f82
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 2 deletions.
2 changes: 1 addition & 1 deletion elm-package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "1.0.0",
"version": "1.1.0",
"summary": "Convenience functions for working with Json",
"repository": "https://github.com/elm-community/json-extra.git",
"license": "MIT",
Expand Down
46 changes: 45 additions & 1 deletion src/Json/Decode/Extra.elm
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module Json.Decode.Extra exposing (date, apply, (|:), set, dict2, withDefault, maybeNull, lazy)
module Json.Decode.Extra exposing (date, apply, (|:), sequence, set, dict2, withDefault, maybeNull, lazy)

{-| Convenience functions for working with Json
Expand All @@ -8,6 +8,9 @@ module Json.Decode.Extra exposing (date, apply, (|:), set, dict2, withDefault, m
# Incremental Decoding
@docs apply, (|:)
# List
@docs sequence
# Set
@docs set
Expand Down Expand Up @@ -285,3 +288,44 @@ lazy getDecoder =
customDecoder value
<| \rawValue ->
decodeValue (getDecoder ()) rawValue


{-| This function turns a list of decoders into a decoder that returns a list.
The returned decoder will zip the list of decoders with a list of values, matching each decoder with exactly one value at the same position. This is most often useful in cases when you find yourself needing to dynamically generate a list of decoders based on some data, and decode some other data with this list of decoders. There are other functions that seem similar:
- `Json.Decode.oneOf`, which will try every decoder for every value in the list, might be too lenient (e.g. a `4.0` will be interpreted as an `Int` just fine).
- `Json.Decode.tuple1-8`, which do something similar, but have a hard-coded length. As opposed to these functions, where you can decode several different types and combine them, you'll need to manually unify all those types in one sum type to use `sequence`.
Note that this function, unlike `List.map2`'s behaviour, expects the list of decoders to have the same length as the list of values in the JSON.
type FloatOrInt
= I Int
| F Float
-- we'd like a list like [I, F, I] from this
-- fairly contrived example, but data like this does exist!
json = "[1, 2.0, 3]"
intDecoder = Decode.map I Decode.int
floatDecoder = Decode.map F Decode.float
decoder : Decoder (List FloatOrInt)
decoder =
sequence [ intDecoder, floatDecoder, intDecoder ]
decoded = Decode.decodeString decoder json
-- Ok ([I 1,F 2,I 3]) : Result String (List FloatOrInt)
-}
sequence : List (Decoder a) -> Decoder (List a)
sequence decoders =
customDecoder
(list value)
(\jsonValues ->
if List.length jsonValues /= List.length decoders then
Err "Number of decoders does not match number of values"
else
List.map2 decodeValue decoders jsonValues
|> List.foldr (Result.map2 (::)) (Ok [])
)

0 comments on commit 1629f82

Please sign in to comment.