Representing atomically updatable RemoteData

Hi. I’m new here.

I’m currently using BuckleScript but due to recent announcements, I’m considering moving to PureScript where at least I can expect some stability. I actually used Elm before BuckleScript but changed course when 0.19 came out. I hope PureScript is my last stop :joy:

I migrated this issue when with me when I moved from Elm to BuckleScript. But I wanted to see if there’s a solution before refactoring to PureScript.

Basically, I have a server resource that’s represented as a list of items. I used RemoteData to represent this list in the front-end. However, the items within the list can be updated individually after a successful fetch. But due to Ajax, I have to wrap the items around Either (or Result in Ocaml). This means that the actual data is wrapped twice and it is thus boilerplaty to handle the data at different points in the codebase; e.g. when updating an item, I have to consider the RemoteData wrapping the list of items.

Would you represent the data differently? If not, do you know a trick/method to reduce the boilerplate?

1 Like

Hi, and welcome!

After reading your post, two points jump to mind. Firstly, is Either the right type for your needs? If your types look like Either SomeData SomeData then probably not. Secondly, transformation of nested data can become much easier with lenses. The hardest part is learning about lenses.

As an alternative to Either, the Lumi component library has a custom type used for tracking state in forms:

data Validated a
  = Fresh a
  | Modified a

This might model the state changes you are trying to represent a bit better. See for more details.

For modifying nested data, if you are using RemoteData and Validated then it’s possible to make functions like:

modifyAt :: forall a e. Int -> (a -> a) -> RemoteData e (Array (Validated a)) -> RemoteData e (Array (Validated a))
modifyAt i f = over (_Success <<< element i traversed) (Modified <<< f <<< view _Validated)


viewAt :: forall a e. Int -> RemoteData e (Array (Validated a)) -> Maybe a
viewAt i = preview (_Success <<< element i traversed <<< _Validated)

The lenses library:

1 Like

Welcome! We moved from BuckleScript to PureScript and have been so much happier!
@robertdp’s suggestion with lenses will get you the most flexibility with the least amount of boilerplate, but if you’re looking for a middle ground with less learning curve, there are some simple tips/tricks that can cut down on the boilerplate, definitely compared to BuckleScript (not sure about Elm though). You might just need more monads :stuck_out_tongue_winking_eye:

grabItem :: Int -> RemoteData Error (Array (Either Error Item)) -> Maybe Item
grabItem index items = do
  xs <- RemoteData.toMaybe items  -- xs is `Array (Either Error Item)`
  eitherX <- xs !! index  -- eitherX is `Either Error Item`
  hush eitherX

successfulItems :: RemoteData Error (Array (Either Error Item)) -> RemoteData Error (Array Item)
successfulItems allItems = Array.mapMaybe hush <$> allItems

renderList :: RemoteData Error (Array (Either Error Item)) -> JSX
renderList (Success items) = fold (either (const errorDiv) renderItem <$> items)
    errorDiv :: JSX -- needs an implementatation
    renderItem :: Item -> JSX -- needs an implementation
renderList (Failure err) = errorPage err
renderList _ -> loadingIcon

I’m sure with targeted examples, we could give some tailored tips/tricks, or @robertdp (or someone else who’s not me :wink:) could explain how lenses work for that case.

Thank you @robertdp @ntwilson ; the examples are very useful

I’ve been putting off learning Lenses for a while now. I’ve used simple lenses from bs-rationale but it isn’t as powerful as purescript-profunctor-lenses; e.g. I couldn’t find a Prism implementation in the Ocaml/BuckleScript ecosystem.

1 Like