john-kelly / elm-postgrest

Make PostgREST requests in Elm
109 stars 14 forks source link

Selecting where order does not matter? #20

Closed john-kelly closed 5 years ago

john-kelly commented 7 years ago

Okay, so in terms of implementing Json without depending on order

[9:52]
Let's start by defining a simple mode:

[9:52]

  { name : String
  , age : Int
  }

[9:53]
Nothing fancy really. Now let's describe how we'd like our decoders to look:

[9:53]

decodeModel value =
  Ok { name = "", age = 0 } 
    |> decodeField "age" Json.int setAge value
    |> decodeField "name" Json.string setName value

[9:53]
Notice how I've gotten rid of the confusing Decoder abstraction for now

[9:53]
Also notice that name and age are out of place

[9:54]
Also notice that I define setAge and setName, lens style

[9:55]
they're mostly boilerplate but for clarity, this is what they look like:

setName model name = { model | name = name }

setAge model age = { model | age = age }

[9:55]
finally, we use it like this:

  E.object [ ("name", E.string "noah"), ("age", E.int 10) ]
    |> decodeModel
    |> toString
    |> Html.text

[9:56]
decodeModel will return a result of Ok { name = "noah", age = 10 }

[9:56]
decodeField is a scary looking thing which is actually pretty simple:

[9:56]

  String
  -> Json.Decoder a 
  -> (Model -> a -> Model) 
  -> Json.Value 
  -> Result String Model 
  -> Result String Model 
decodeField fieldName decoder setter value model = 
  case model of 
    Err s -> model
    Ok v -> 
      Json.decodeValue (Json.field fieldName decoder) value
        |> Result.map (setter v)

[9:56]
(there are various ways of cleaning this up some more, but this is how you do out-of-order decoding in elm)

[9:59]
It could be further simplified if elm provided a couple of things. Namely, things that were taken out of the type system that didn't need to be (e.g adding fields to a record - then we wouldn't need to provide a default)

[9:59]
This approach is infinitely more workable.

[10:00]
You can make refactoring changes to your model without worrying about the order

[10:00]
If you have the right tooling, then generating setters/getters is trivial

[10:00]
Anyway, the other point: notice how I flatten the types as soon as possible.

[10:01]
People get so confused when they see Decoder a and don't know how to make their own

fredcy [10:01 AM]
Fascinating. But my brain hurts. I hope someday you'll write this up. (I know that's not your thing though).

eeue56 [10:02 AM]
Which bit is making your brain hurt? Maybe I can explain that a bit better

fredcy [10:04 AM]
The role of the first line of decodeModel puzzles me. Just haven't seen record values built up from a default as that appears to be.

rtfeldman [10:12 AM]

AndMap (not a big fan of the name either) is exactly the right tool for the job of course, the json-pipeline library aliases andMap, but to understand how that decoder works, you need to know how andMap works

this is not a great pitch for andMap the function itself as opposed to the concept behind it :wink:

eeue56 [10:15 AM]
@fredcy, it's exactly what decode does in elm-decode-pipeline

glenjamin [10:15 AM]
@eeue56 when thinking about it, I found the Generator one harder to refactor (only tried in my head) because of the need to chain the RNG state around - did I miss something?

eeue56 [10:15 AM]
You can think of it as "raising" or "lifting" a value into a type. Basically, just taking a Model and turning into Result x Model

[10:16]
which can then be piped down to the other functions, so they can keep a clean sig

[10:16]
@glenjamin you can pass the state onwards in the same way through a tuple if you want

[10:16]
I just didn't for the decode example since it doesn't need it

glenjamin [10:17 AM]
ah right, same general approach then - you get something like bind and return, but name them based on the concrete case?

eeue56 [10:19 AM]
you'd need a return for that example yeah

john-kelly commented 7 years ago

@eeue56 Oh! I apologize. I was just copy-pasting a conversation from slack in here. I did not mean to tag anyone!

fredcy commented 7 years ago

Aha. I wondered what I had to do with this repo.

john-kelly commented 7 years ago

https://medium.com/@eeue56/json-decoding-in-elm-is-still-difficult-cad2d1fb39ae#.ycwubyz8v