How to update a deeply nested union type based on UI updates?

I’m experimenting with Elm at my company and I’m trying to rewrite a section of the UI in Elm.

The UI

conditions-actions-ui

is generated from the following JSON

// conditions
{"$and":[{"sales_transaction.source_location_id":{"$in":["1"]}}]}

// actions
[{"type":"DiscountItemWithRequiredPurchase","params":{"discount":"0","discount_type":"free","item_conditions":{"$and":[{"item_line.item_primary_vendor_id":{"$in":["100002"]}},{"item_line.item_custom.color":{"$in":["Black","BLACK"]}}]},"required_qty":"1","discount_qty":"1"},"id":"71993826-c9af-11e4-9bb1-bc764e2061ef"}]

Here’s what I plan to do:

  1. Parse the JSON into a union type. Let’s call it the AST.
  2. Use the AST to generate the UI.
  3. Modify the AST whenever a change is made to the UI.
  4. On save, convert the AST back to JSON and send to the server.

Questions:

  1. Does my plan make sense or sound reasonable?
  2. How would I go about making updates deep in the AST when a change is made via the UI?
  3. Related to part 2, maybe the AST as a union type is not the best representation since I’d need to make many updates to it as the user messes around with the UI. What then might be a better data structure?

Thanks for any help on this problem.

1 Like

Very cool!

Personally I’d go with a modification of the plan:

  1. Design a union type to model the UI
  2. Modify it whenever a change is made to the UI
  3. Work on parsing the JSON into that union type
  4. On save, serialize it to JSON and send to the server

I think it’ll be easier to discuss that in the context of some preliminary code. What would the type declaration for your ideal data model to represent this UI (ignoring JSON for now) look like?

3 Likes

For the union type, I’m thinking of starting with something along the lines of:

type Expr
  = And (List Term)
  | Or (List Term)

type Term
  = Condition Condition
  | Group Expr

type alias Condition =
  { term : String
  , guard : Guard
  }

type Guard
  = In (List Value)
  | NotIn (List Value)
  | GreaterThanEqualTo Int
  | GreaterThan Int
  | LessThanEqualTo Int
  | LessThan Int

type Value
  = VString String
  | VInt Int

So the following UI

Screenshot%20from%202018-02-02%2013%3A04%3A05

which has the JSON

{
  "$and": [
    { "sales_transaction.source_location_id": { "$in": ["1"] } },
    { "sales_transaction.subtotal": { "$gte": 100 } },
    { "$or": [
      { "sales_transaction.total_qty": { "$gte": 5 } },
      { "$and": [
        { "customer.custom.is_employee": { "$in": ["Yes"] } }
      ]}
    ]}
  ]
}

would be represented by

expr : Expr
expr =
  And
    [ Condition { term = "sales_transaction.source_location_id", guard = In [ VString "1" ] }
    , Condition { term = "sales_transaction.subtotal", guard = GreaterThanEqualTo 100 }
    , Group <| Or
      [ Condition { term = "sales_transaction.total_qty", guard = GreaterThanEqualTo 5 }
      , Group <| And
        [ Condition { term = "customer.custom.is_employee", guard = In [ VString "Yes" ] } ]
      ]
    ]

P.S. @rtfeldman Thinking about this some more I realized that I still didn’t follow your advice of designing the union type from the UI and not the JSON. So I’d go back to your step 1 and try that and let you know how it goes. Thanks for the help.

1 Like