How do I express the following idea in Haskell? While, the syntax is completely made up, here's what I'm trying to achieve:
- My app has highly nested core data-types with each "level" having FromJson/ToJson instances
- The JSON API powering the UI has the ability to manipulate individual "levels of nesting", eg. to edit the address, you don't need to edit the complete order.
- However, I want to make sure that along with the data that was modified by the UI, the complete order is also sent back. This ensures that, if the edit has resulted in some dependent field in another part of the object being changed, it is communicated back to the UI.
Edit: The core question is not about the app logic, per-se. The core question is how to represent JSON keys in a type-safe manner while having the ability to parameterise over them. The simple solution is to have a different concrete type for each API return type, eg {orderItems :: [OrderItem], order :: Order}
or {address :: Address, order :: Order}
or {email :: Email, customer :: Customer}
. But these will get repetitive quickly. I want to have a data-type which represents the idea of a JSON with a primary key-value pair and a secondary/supporting key-value pair, where the key names can be easily changed.
The pseudo-code given below is a generalisation of this idea:
data IncomingJson rootname payload = (FromJson payload, ToString rootname) => IncomingJson
{
rootname :: payload
}
data OutgoingJson rootname payload sidename sidepayload = (ToJson payload, ToString rootname, ToJson sidepayload, ToString sidename) => IncomingJson
{
rootname :: payload
, sidename :: sidepayload
}
createOrder :: IncomingJson "order" NewOrder -> OutgoingJson "order" Order Nothing ()
editOrderItems :: IncomingJson "items" [OrderItem] -> OutgoingJson "items" [OrderItem] "order" Order
editOrderAddress :: IncomingJson "address" Address -> OutgoingJson "address" Address "order" Order
(Edit: an attempt at a full answer to the revised question.)
The example code below may be close to what you want. This example defines
OutgoingJSON
andIncomingJSON
with customToJSON
andFromJSON
instances respectively. (I included aToJSON
for theIncomingJSON
datatype, too, though I suspect you don't need it.) It relies on each datatype being assigned a JSON key via a shortKeyedJSON
instance. It's possible to useGHC.Generics
or some alternative to automate this, but that seems both ugly and ill-advised. (You don't really want your JSON keys directly tied to Haskell data type names, do you?)If you load this up and look at the types of
inExample1
andoutExample1
, they should match what you expect.inExample2
andinExample3
demonstrate type-safe parsing of a block of JSON -- it succeeds if the key for the expected type exists in the JSON block and fails if it doesn't. Finally,outExample1AsJSON
shows how anOutgoingJSON
example will be serialized with the desired primary and secondary keys.