reflex-frp / reflex

Interactive programs without callbacks or side-effects. Functional Reactive Programming (FRP) uses composable events and time-varying values to describe interactive systems as pure functions. Just like other pure functional code, functional reactive code is easier to get right on the first try, maintain, and reuse.

Home Page:https://reflex-frp.org

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

More flexible collection management

bradrn opened this issue · comments

Currently Reflex supports managing collections of widgets via the various functions in Reflex.Collection, roughly:

listHoldWithKey        :: Ord k => Map k v   -> Event  (Map k (Maybe v))  -> (k ->         v -> m        a ) -> m (Dynamic (Map k a))
listWithKey            :: Ord k =>              Dynamic (Map k v)         -> (k -> Dynamic v -> m        a ) -> m (Dynamic (Map k a))
list                   :: Ord k =>              Dynamic (Map k v)         -> (     Dynamic v -> m        a ) -> m (Dynamic (Map k a))
simpleList             ::                       Dynamic [v]               -> (     Dynamic v -> m        a ) -> m (Dynamic       [a])
listViewWithKey        :: Ord k =>              Dynamic (Map k v)         -> (k -> Dynamic v -> m (Event a)) -> m (Event   (Map k a))
listWithKeyShallowDiff :: Ord k => Map k v   -> Event (Map k (Maybe v)) -> (k ->         v -> Event   v    -> m a)         -> m (Dynamic (Map k a))
selectViewListWithKey_ :: Ord k => Dynamic k -> Dynamic (Map k v)       -> (k -> Dynamic v -> Dynamic Bool -> m (Event a)) -> m (Event k)

These work well enough for simple interfaces. Unfortunately, they have a major problem: they are all list functions. That is, Reflex currently renders the m a widgets by running them sequentially in the order specified by the map keys; there is no way to render them in any other order. Accordingly, it can be difficult to dynamically manage tables or any other sort of widget layout.

Additionally, these functions make it difficult to do anything aside from simply adding widgets to the end and removing widgets from anywhere. Inserting an element in the middle of a map is difficult, for instance, and swapping two widgets is completely impossible.

In theory, I should be able to write a replacement function myself which does what I want. In practice, this requires dealing with Adjustable, a typeclass with practically no documentation whatsoever (#418), as well as various other corners of the library with just as little documentation (e.g. Incremental). I only even know that much from reading the definition of listWithKey. Basic widget manipulation should not require reading the source code of the GUI library!

Accordingly, it would be nice to have a new, fully general function for collection management, one which supports the manipulations I mentioned above. I suggest something like the following:

collectionHoldWithKey :: Ord k => Map k v -> Event (PatchMapWithMove k v) -> (k -> v -> m a) -> (Map k (m a) -> m (Map k a)) -> m (Dynamic (Map k a))

The idea here would be that the final parameter provides a method for collecting each individual widget into a larger widget, allowing control of sequencing etc. But I’m not sure if this would work as is.

I have experimented with this a bit:

https://gist.github.com/tek/d427c0fd9f8650892d3c469b63b55175

I also tried to use PatchDMapWithMove, but the entries aren't reordered when drawing the list with reflex-dom:

https://gist.github.com/tek/1ea3d43127d5f17fafbfd139f169b269

there is no way to render them in any other order

If you’re using reflex-dom you can control order with css via flex-order

https://gist.github.com/tek/d427c0fd9f8650892d3c469b63b55175

This looks interesting, but I’m not quite sure what it does. What is SortedUpdate, and how does patchableList differ from listHoldWithKey?

If you’re using reflex-dom you can control order with css via flex-order

Unfortunately, I’m not using flexbox. And even if I were, this wouldn’t solve all my problems, since it will still always render in a list.

@bradrn

This looks interesting, but I’m not quite sure what it does. What is SortedUpdate, and how does patchableList differ from listHoldWithKey?

The type of the update elements in the Event passed to the widget is different from the one that is used to create it.
SortedUpdate contains either a Left with an update, a Right . Just for a new element or a Right Nothing for a deletion.