# Inside a decoder, create a tuple with Decode.Value and its decoded version

**URL:** <https://discourse.elm-lang.org/t/inside-a-decoder-create-a-tuple-with-decode-value-and-its-decoded-version/4913>\
**Category:** Learn\
**Created:** [January 4, 2020, 11:53am UTC](https://discourse.elm-lang.org/t/inside-a-decoder-create-a-tuple-with-decode-value-and-its-decoded-version/4913 "2020-01-04T11:53:17Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![rafal](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rafal/32/1278_2.png) [@rafal](https://discourse.elm-lang.org/u/rafal)\
**Post date:** [January 4, 2020, 11:53am UTC](https://discourse.elm-lang.org/t/inside-a-decoder-create-a-tuple-with-decode-value-and-its-decoded-version/4913/1 "2020-01-04T11:53:17Z")

</div>

[I have a file decoder](https://github.com/ravicious/konbikol/blob/bfedb61563b554b545a325ad33ee6b8db5894b9c/src/Main.elm#L126-L137) to which I now want to add drag and drop support. I didn’t need the decoded file up until now, because in the update function I just sent the file through a port as Decode.Value.

So in order to support drag and drop, I changed this:

```
fileDecoder : Decode.Decoder Msg
fileDecoder =
    Decode.at ["target", "files"] (Decode.oneOrMore (\firstFile rest -> GotFile firstFile) Decode.value)

```

To this:

```
fileDecoder : Decode.Decoder Msg
fileDecoder =
    let
        keepFirstFile =
            \firstFile rest -> firstFile
    in
    Decode.oneOf
        [Decode.at [ "target", "files"] (Decode.oneOrMore keepFirstFile Decode.value) -- input[type=file]
        , Decode.at ["dataTransfer", "files"] (Decode.oneOrMore keepFirstFile Decode.value) -- drag and drop
        ]
        |> Decode.map GotFile

```

The problem with this is that I need to be sure the file is a PDF file. For `<input>` I just have the `accept` attribute, but drag and drop has no such thing.

Now, there’s a myriad of other ways I could solve my actual problem, for example grabbing the Decode.Value that comes out of the decoder and decoding it to a file in the update function. However, I would like to keep all the file processing inside the decoder, so I wanted to add something like this:

```
fileDecoder : Decode.Decoder Msg
fileDecoder =
    let
        keepFirstFile =
            \firstFile rest -> firstFile
    in
    Decode.oneOf
        [Decode.at [ "target", "files"] (Decode.oneOrMore keepFirstFile Decode.value) -- input[type=file]
        , Decode.at ["dataTransfer", "files"] (Decode.oneOrMore keepFirstFile Decode.value) -- drag and drop
        ]
        |> Decode.map2 Tuple.pair File.decoder
        |> Decode.andThen
            (\( file, fileValue ) ->
                if File.mime file == "application/pdf" then
                    Decode.succeed fileValue

                else
                    Decode.fail "Not PDF"
            )
        |> Decode.map GotFile

```

But this doesn’t work at all. Matter of fact, it appears as if the decoder is not even executed. I can stick `|> Decode.map (Debug.log "decoder")` into all possible places in the pipeline, but no log message is emmited. If I remove the `map2` and `andThen` calls, I can see the logs.

So, what’s going on here? Is there a way to debug this?

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [January 4, 2020, 2:15pm UTC](https://discourse.elm-lang.org/t/inside-a-decoder-create-a-tuple-with-decode-value-and-its-decoded-version/4913/2 "2020-01-04T14:15:40Z")

</div>

`|> Decode.map2 Tuple.pair File.decoder` does not do what you think it does.  
It will try to decode the whole `fileDecoder` input value as a file, not the value of `dataTransfers.files` which is never used by `File.decoder` (it is just put in a tuple with it).

You may want something like the following instead:

```elm
fileDecoder : Decoder Msg
fileDecoder =
    let
        keepFirstFile =
            \firstFile rest -> firstFile
    in
    Decode.oneOf
        [Decode.at [ "target", "files"] (Decode.oneOrMore keepFirstFile Decode.value) -- input[type=file]
        , Decode.at ["dataTransfer", "files"] (Decode.oneOrMore keepFirstFile Decode.value) -- drag and drop
        ]
        |> Decode.andThen
            (\value ->
                case Decode.decodeValue (Decode.map File.mime File.decoder) value of
                    Ok "application/pdf" ->
                        Decode.succeed value

                    _ ->
                        Decode.fail "Not PDF"
            )
        |> Decode.map GotFile

```

Note that your `keepFirstFile` function is the same as [`Basics.always`](https://package.elm-lang.org/packages/elm/core/latest/Basics#always), your name is more explicit though in this context.

---

<div class="post-metadata">

**Author:** ![rafal](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rafal/32/1278_2.png) [@rafal](https://discourse.elm-lang.org/u/rafal)\
**Post date:** [January 4, 2020, 2:41pm UTC](https://discourse.elm-lang.org/t/inside-a-decoder-create-a-tuple-with-decode-value-and-its-decoded-version/4913/3 "2020-01-04T14:41:59Z")

</div>

Thanks, that should work! I thought that with the right function calls I could avoid calling `decodeValue` inside the decoder, but there’s nothing wrong with that really.

> [@dmy](#):
>
> `|> Decode.map2 Tuple.pair File.decoder` does not do what you think it does.  
> It will try to decode the whole `fileDecoder` input value as a file, not the value of `dataTransfers.files` which is never used by `File.decoder` (it is just put in a tuple with it).

Right, I looked at the docs of `map2` and saw that it’s used mostly for composing finished records and I thought that instead of a record I could just use a tuple. What I forgot about is exactly what you said. After all, even the docs for `map2` have this example: `map2 Point (field "x" float) (field "y" float)`, which is basically “grab this field from the entire JSON and then the other field from the entire JSON and pass them as arguments to `Point`”.

I think my problem was that I thought I can somehow within the decoding pipeline get a value using one decoder and then “map” the value through another decoder – but that’s not possible without using `andThen` and “manually” calling `decodeValue`, since `map` only works on the value that’s within the context of the decoder. It was just a mismatch of what I thought the API allows me to do vs what it’s actually for.

---

<div class="post-metadata">

**Author:** ![dmy](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/dmy/32/702_2.png) [@dmy](https://discourse.elm-lang.org/u/dmy)\
**Post date:** [January 4, 2020, 3:12pm UTC](https://discourse.elm-lang.org/t/inside-a-decoder-create-a-tuple-with-decode-value-and-its-decoded-version/4913/4 "2020-01-04T15:12:18Z")

</div>

I see. Then you can avoid the `decodeValue` by decoding together the `File` and the `value`:

```elm
fileDecoder : Decoder Msg
fileDecoder =
    let
        firstFileAndValueDecoder =
            Decode.oneOrMore (\first rest -> first)
                (Decode.map2 Tuple.pair File.decoder Decode.value)
    in
    Decode.oneOf
        [Decode.at [ "target", "files"] firstFileAndValueDecoder -- input[type=file]
        , Decode.at ["dataTransfer", "files"] firstFileAndValueDecoder -- drag and drop
        ]
        |> Decode.andThen
            (\( file, value ) ->
                if File.mime file == "application/pdf" then
                    Decode.succeed value

                else
                    Decode.fail "Not PDF"
            )
        |> Decode.map GotFile

```

---

<div class="post-metadata">

**Author:** ![rafal](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/rafal/32/1278_2.png) [@rafal](https://discourse.elm-lang.org/u/rafal)\
**Post date:** [January 4, 2020, 4:12pm UTC](https://discourse.elm-lang.org/t/inside-a-decoder-create-a-tuple-with-decode-value-and-its-decoded-version/4913/5 "2020-01-04T16:12:55Z")

</div>

Oh wow, this is just wonderful. 😃 Thanks!

---

<div class="post-metadata">

**Author:** ![system](https://canada1.discourse-cdn.com/flex035/uploads/elm_lang/original/1X/50a05e53677a2c3b47776d7abd0f113eb50193a1.png) [@system](https://discourse.elm-lang.org/u/system)\
**Post date:** [January 14, 2020, 4:12pm UTC](https://discourse.elm-lang.org/t/inside-a-decoder-create-a-tuple-with-decode-value-and-its-decoded-version/4913/6 "2020-01-14T16:12:58Z")

</div>

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.
