# JSON Decode to Opaque Type

**URL:** <https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959>\
**Category:** Request Feedback\
**Created:** [February 21, 2021, 3:05pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959 "2021-02-21T15:05:22Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![Joe\_Daku](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joe_daku/32/3852_2.png) [@Joe\_Daku](https://discourse.elm-lang.org/u/Joe_Daku)\
**Post date:** [February 21, 2021, 3:05pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/1 "2021-02-21T15:05:23Z")

</div>

Hi all,

Pretty new to Elm and am having trouble figuring this out. I’ve been provided with (correct me if I am wrong) an opaque type and need to create a JSON Decoder for it.

Here’s a really stripped down example of what I’m working with:

```
type SomeType = SomeType
    { value1: String
    , value2: Int
    }

newSomeType = SomeType
    { value1 = ""
    , value2 = 0
    }

withValue1 : String -> SomeType -> SomeType
withValue1 value1 (SomeType someType) = SomeType { someType | value1 = value1}

withValue2 : Int -> SomeType -> SomeType
withValue2 value2 (SomeType someType) = SomeType { someType | value2 = value2}

```

It feels like there should be some elegant way to combine the builders with the individual field decoders into a nice pipeline… but I’ve been struggling with it.

```
someTypeDecoder : Decoder SomeType
someTypeDecoder = ???

result : Result Error SomeType
result = Decode.decodeString someTypeDecoder json

json : String
json = """
{
  "value1": "hello",
  "value2": 123
}
"""

```

Thanks for any help 🙂

---

<div class="post-metadata">

**Author:** ![Joe\_Daku](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joe_daku/32/3852_2.png) [@Joe\_Daku](https://discourse.elm-lang.org/u/Joe_Daku)\
**Post date:** [February 21, 2021, 4:06pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/2 "2021-02-21T16:06:30Z")

</div>

Ok, so I’ve continued to bash my head at this and feel like I am getting closer (works but not as pretty as I’d like)…

```
decodeField : Decoder a -> (model -> a -> model) -> Decoder model -> Decoder model
decodeField fieldDecoder fieldSetter modelDecoder = Decode.map2 fieldSetter modelDecoder fieldDecoder

someTypeDecoder : Decoder SomeType
someTypeDecoder = Decode.succeed newSomeType
    |> decodeField (Decode.field "value1" Decode.string) (\model value1 -> withValue1 value1 model)
    |> decodeField (Decode.field "value2" Decode.int) (\model value2 -> withValue2 value2 model)

```

I think it can still be cleaned up. I’d like to get it to the point where I just pass the field decoder and setter

---

<div class="post-metadata">

**Author:** ![Joe\_Daku](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joe_daku/32/3852_2.png) [@Joe\_Daku](https://discourse.elm-lang.org/u/Joe_Daku)\
**Post date:** [February 21, 2021, 4:34pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/3 "2021-02-21T16:34:37Z")

</div>

Well this has been a one sided conversation… but I did it 😃  
Maybe this can act as a reference for someone. Also open to feedback if this looks completely wrong or bad practice.

```
someTypeDecoder : Decoder SomeType
someTypeDecoder = Decode.succeed newSomeType
    |> decode withValue1 (Decode.field "value1" Decode.string)
    |> decode withValue2 (Decode.field "value2" Decode.int)

decode : (a -> model -> model) -> Decoder a -> Decoder model -> Decoder model
decode fieldSetter fieldDecoder modelDecoder = Decode.map2 (flip fieldSetter) modelDecoder fieldDecoder

flip : (a -> b -> c) -> b -> a -> c
flip function b a = function a b
```

---

<div class="post-metadata">

**Author:** ![terezka](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/terezka/32/579_2.png) [@terezka](https://discourse.elm-lang.org/u/terezka)\
**Post date:** [February 21, 2021, 4:44pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/4 "2021-02-21T16:44:31Z")

</div>

Hi Joe!

Welcome! Your solution is great! It’s actually quite the library commonly used [elm-json-decode-pipeline](https://package.elm-lang.org/packages/NoRedInk/elm-json-decode-pipeline/latest/) package. If I understand your problem correctly, then the native solution would be:

```auto
someTypeDecoder : Decoder SomeType
someTypeDecoder =
  Decode.map2 SomeType
    (Decode.field "value1" Decode.string)
    (Decode.field "value2" Decode.int)

```

and using the common [elm-json-decode-pipeline](https://package.elm-lang.org/packages/NoRedInk/elm-json-decode-pipeline/latest/) package:

```auto
import Json.Decode.Pipeline as Pipeline

someTypeDecoder : Decoder SomeType
someTypeDecoder =
  Decode.succeed SomeType
    |> Pipeline.required "value1" Decode.string
    |> Pipeline.required "value2" Decode.int

```

Is this helpful? Do you have any other questions? The source for the central trick in `elm-json-decode-pipeline` is [here](https://github.com/NoRedInk/elm-json-decode-pipeline/blob/1.0.0/src/Json/Decode/Pipeline.elm#L224)!

EDIT: On second read, are you looking to add default values? (This ones in `newSomeType`?) In that case, you could do:

```elm
someTypeDecoder : Decoder SomeType
someTypeDecoder =
  Decode.succeed SomeType
    |> Pipeline. optional "value1" Decode.string ""
    |> Pipeline. optional "value2" Decode.int 0

```

or use [`nullable`](https://package.elm-lang.org/packages/elm/json/latest/Json-Decode#nullable) or [`maybe`](https://package.elm-lang.org/packages/elm/json/latest/Json-Decode#maybe) in combination with `Maybe.withDefault` for a `elm/json` only approach!

---

<div class="post-metadata">

**Author:** ![Joe\_Daku](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joe_daku/32/3852_2.png) [@Joe\_Daku](https://discourse.elm-lang.org/u/Joe_Daku)\
**Post date:** [February 21, 2021, 4:51pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/5 "2021-02-21T16:51:57Z")

</div>

The problem was that the type is opaque, so the simple solution you posted doesn’t work.  
If SomeType were like this, it’d be fine:  
type alias SomeType =  
{ value1: String  
, value2: Int  
}  
Unfortunately it is not and I cannot change it in this case.

---

<div class="post-metadata">

**Author:** ![terezka](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/terezka/32/579_2.png) [@terezka](https://discourse.elm-lang.org/u/terezka)\
**Post date:** [February 21, 2021, 4:52pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/6 "2021-02-21T16:52:37Z")

</div>

Ah, I see, sorry I totally missed that detail!

---

<div class="post-metadata">

**Author:** ![Joe\_Daku](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/joe_daku/32/3852_2.png) [@Joe\_Daku](https://discourse.elm-lang.org/u/Joe_Daku)\
**Post date:** [February 21, 2021, 4:55pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/7 "2021-02-21T16:55:14Z")

</div>

No worries, glad I figured this out. Elm is really a pleasure to learn.  
The compiler has been a huge help in getting here.

---

<div class="post-metadata">

**Author:** ![terezka](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/terezka/32/579_2.png) [@terezka](https://discourse.elm-lang.org/u/terezka)\
**Post date:** [February 21, 2021, 4:59pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/8 "2021-02-21T16:59:42Z")

</div>

So glad to hear! 🙂

---

<div class="post-metadata">

**Author:** ![allanderek](https://yyz1.discourse-cdn.com/flex035/user_avatar/discourse.elm-lang.org/allanderek/32/1360_2.png) [@allanderek](https://discourse.elm-lang.org/u/allanderek)\
**Post date:** [February 22, 2021, 2:43pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/9 "2021-02-22T14:43:32Z")

</div>

Hi Joe,

First of all, it’s not quite what I would call an _Opaque_ type. An opaque type is one in which you cannot see the details. You achieve this in Elm via not exposing the constructors of the opaque type:

```elm
module SomeType exposing (SomeType) -- Note, not SomeType(..)

type SomeType =
    SomeType { value1 : String, value2 : Int }

```

Because of this, you would have to provide someway to _create_ and use values within that module. The reason for doing this would be because there is some way to create an **invalid** value that you want to _make impossible_, so for example you might do something like this:

```elm
module Length exposing
    ( Length
    , fromInt
    , toInt
    )

type Length = Length Int

fromInt : Int -> Length
fromInt i =
    -- So it is impossible to create a negative length.
    Length (abs i)

toInt : Length -> Int
toInt l =
    case l of
         Length i ->
              i

```

Because it is impossible to create a value of type `Length` from outside of this module, we know that the only way to create a `Length` is by using `Length.fromInt` and therefore we know that all `Length`s in this program will be non-negative. That’s what an opaque type is **for me** , but do note that there isn’t always universal agreement on typing terms.

The `SomeType` in your program, may or may not be oqaque from that definition, but even if is, you would need some way to create values of that type anyway. In addition, you would likely keep the definition of the decoder in the same module, so as far as that was concerned it wouldn’t be opaque. The `SomeType` in your program is what is generally called in Elm-land as a `Custom` type, or a `Custom tagged` type. What we mean is _‘one of those types that has a set of constructors in its type definition’_.

Now, your actual question. As was suggested, if your `SomeType` was a type alias to a record type you could do the following:

```elm
someTypeDecoder : Decoder SomeType
someTypeDecoder =
  Decode.map2 SomeType
    (Decode.field "value1" Decode.string)
    (Decode.field "value2" Decode.int)

```

You only need to tweak this a little bit in your case. The key point is that the first argument to `Decode.map2` requires the type `(a -> b -> c)`, which in this case is instantiated to `String -> Int -> SomeType`. You can give **any** function with that type, but the most obvious one would be:

```elm
someTypeDecoder : Decoder SomeType
someTypeDecoder =
  let
       createSomeType value1 value2 =
           SomeType { value1 = value1, value2 = value2 }
  in
  Decode.map2 createSomeType
    (Decode.field "value1" Decode.string)
    (Decode.field "value2" Decode.int)

```

You can do the analogous with the `Pipeline` version.

---

<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:** [March 4, 2021, 2:44pm UTC](https://discourse.elm-lang.org/t/json-decode-to-opaque-type/6959/10 "2021-03-04T14:44:28Z")

</div>

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