-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JSON decoders chapter #68
base: main
Are you sure you want to change the base?
Conversation
JSON Decoding
Improvements wip
Comment out boolean.
json/DataTypes/DecodeStudent.roc
Outdated
nameField = field "name" string | ||
creditsField = field "credits" number | ||
enrolledField = field "enrolled" bool | ||
(map3 nameField creditsField enrolledField \(name, credits, enrolled) -> { name: name, credits: credits, enrolled: enrolled }) json |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't know if you want to get into record builder syntax in this chapter (or if it seems too advanced - also, the new version of this syntax just landed in the compiler, so it may not have existed when this part of the chapter was first written!) but it can make this function a lot more concise:
readModule : JsonDecoder Module
readModule = \json ->
{ map2 <-
name: field "name" string,
credits: field "credits" number,
enrolled: field "enrolled" bool,
} json
By the way, I know we have a compiler bug that means right now you have to do \json -> (...) json
(which in general should be able to be written as just (...)
), but hopefully we can fix that in time to simplify this to just readModule = { map2 <- ... }
without the need for the enclosing lambda!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, although I think in order for this to work, map2
would need the function it accepts to take 2 arguments instead of a tuple:
-map2 : JsonDecoder a, JsonDecoder b, ((a, b) -> c) -> JsonDecoder c
+map2 : JsonDecoder a, JsonDecoder b, (a, b -> c) -> JsonDecoder c
That's the map2
signature the { foo <-
syntax sugar is designed to work with 😄
json/index.md
Outdated
--- | ||
--- | ||
# Goals of chapter | ||
I want to show a scale model of how you could JSON decoding (and encoding) in Roc. Doing so will demonstrate using ADTs for modelling your data, pattern matching, function reuse and how to build a small Codec libary. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor terminology note for the actual text of the chapter - in all the Roc documentation we talk about "tag unions" rather than referring to them as ADTs (OCaml programmers might point out that they're more like polymorphic variants anyway), so it'd be good to keep consistent with that terminology!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
okay sure - so does it make sense to say a tag union is a sum of tag types? where each tag can be a value by itself or it can have a payload associated with it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd say that's accurate, although in my experience anyone who's familiar with the term "sum types" understands them right away and it doesn't matter what explanation I use, so I try to focus on people who are used to languages that don't have any form of the feature. 😄
I like to talk about it in terms of "alternatives" - like "this type could be one of several alternatives..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
right gotcha that makes sense!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is looking awesome @monmcguigan, I'm really excited about this chapter!!!
Thanks so much for all your excellent work on it!
Please have a look at my simple JSON decoder implementation and let me know if I'm making any glaringly obvious mistakes - would love feedback on if how I am doing this is idiomatic or not.
Goals of chapter
I want to show a scale model of how you could JSON decoding (and encoding) in Roc. Doing so will demonstrate using ADTs for modelling your data, pattern matching, function reuse and how to build a small Codec libary.
This article was a great help in thinking about how to teach people these concepts.
Code Structure
Student.roc
this is my domain data type of what I want to decode into.JsonData.roc
this is my ADT for JSON. I have purposefully not made it an accurate representation of the JSON data type as it adds a lot of noise for not much gain.DecodeStudentLong.roc
is the verbose version of a JSON decoder where the error handling code and other boilerplate-y code is all tangled up in the business logic. The purpose of it is to highlight lots of the repeated code, so then we can refactor out patterns that we spot.Decoding.roc
this is the meat of what I want to show. I have deocders for myJson
toa
, along with some helper functions. Still to do would be a decoder for Sum types and Product types (see below for more).DecodeStudent.roc
is the final, much simpler Student decoder, which utilises theDecoding.roc
code.Encoding.roc
this is still a work in progress, and isn't going to be included in my talk, but hopefully the chapter (possibly starting with it as it is a simpler thing to get your head around).json/main.roc
here I test both my verbose and simple decoders forStudent
TODOs
sumDecoder
- I am thinking passing in a list ofJsonDecoder a
, wherea
is the super type, and trying each of them till one is successfulproductDecoder
- I think for this aList (Str JsonDecoder a)
would make sense as an input, where each item in the list is the field name and the Json Decoder respectively. But not sure how I can build a record from that and/or how to handle an error if one of the decoders fails.