Closed ajnsit closed 5 years ago
@ajnsit This is an interesting idea. I've taken a look at your formless-independent repo and I see how many of the parts of Formless can be independent of the UI library. I'm curious how far that idea can go -- could we put together a version which can be used in react
, react-basic
, concur
, and halogen
without requiring a ton of effort implementing each ui-specific version?
Formless might be a bit too big for me to get a handle on that, but I'd like to experiment with making a smaller renderless component and seeing whether it can be extended to cover multiple UI libraries without having to make compromises about how the library functions. On the other hand, it may turn out that a smaller component wouldn't be big enough -- that the advantages of doing this don't show up until there's enough logic outside the component to warrant the effort.
I would love to have the same core code usable in each popular UI library so long as it doesn't make development and maintenance significantly more complex. I'm not sure I'm up to the task of maintaining a constellation of libraries for each renderless component :)
Mind if I get back to you this upcoming work week with a preliminary piece of work that you can help me expand out? Having a small example will make it easier for me to understand the scope of this change.
Yeah that sounds good! I would be happy to help build those examples, and once the core is abstracted, I volunteer to build/maintain the concur
and react
(and maybe more) specific versions.
To demonstrate formless-independent bindings to multiple UI libraries, I went ahead and created examples for Concur and React.
There are two issues so far -
The generic dropdown menu UI component I created (Concur version, and React version) seems to misbehave a bit. This might be a problem with the menu component itself, or a problem with my formless extraction. I will look into this later.
Far too much type level information about the internals of Formless is needed to create the menu component. See all the Cons
and Newtype
constraints in the type signature for menu
. I believe this would be a problem even with the Halogen version of Formless. Is there a way we can avoid it, and still manage to write an abstract component which plugs seamlessly into Formless?
Hi @ajnsit! Thanks for doing this. I've also invited you to a repo tracking a small experiment in expressing common Halogen, React, and React Basic patterns in terms of each other to make sure the consequences of a framework-independent component are well understood. For example, how do you make sure you can handle:
componentDidUpdate
or receive
among frameworks?)With the idea being that you can express an idiomatic component in Halogen, React, React Basic, and Concur if you can translate the reasonably common patterns among them, at which point it becomes really useful to have an abstracted core. I'm feeling more confident that this can be done over time.
I quite like what you've done with formless-independent
and I'm glad you were able to quickly get it working with Concur and React. I think the monorepo approach is the right one as well, for maintenance reasons, though it does mean including unnecessary implementations in your project. That said, I believe the compiler and bundlers are able to strip that dead code.
I'm not convinced that the current implementation ticks all the boxes in keeping the core flexible and easy to continue working on without compromising functionality. For example, sometimes you need to imperatively submit a form with SubmitReply
because, for example, you've got two forms in the page and one button needs to trigger both of them. That's done using request-style queries in Halogen and I think can be done using refs in React, but it's important to still be able to support those sorts of features.
I'm continuing to work on a much tinier (just an overblown counter) example component which supports the common features, idioms, and lifecycle events of React and Halogen (I'd like to add Concur as well, but I'd need a little help from you I think). That includes documenting how you can write a core which preserves these features which will be useful for future work on other components, too.
Do you mind waiting a little bit for that to reach fruition? I'd like to feel confident that taking this framework-agnostic approach is not going to compromise future development of the core (make some reasonable features unsupportable, etc.). At that point I'd love to work with you on converting the project wholesale to be agnostic and have Halogen be just one of several implementations.
Sorry for such a long message! With regards to the type level information required to create a menu component -- you're right, they're awful. This is from the React menu:
menu
:: forall opt s form e o restF restI inputs fields
. IsSymbol s
=> IsOption opt
=> BoundedEnum opt
=> Newtype (form Record F.FormField) (Record fields)
=> Cons s (F.FormField e opt o) restF fields
=> Newtype (form Variant F.InputFunction) (Variant inputs)
=> Cons s (F.InputFunction e opt o) restI inputs
=> form Record F.FormField
-> SProxy s
-> (F.Query form -> Effect Unit)
-> React.ReactClass {}
Most of this is confirmation that the symbol s
really is in the form and you're guaranteed that you can retrieve it no matter the form or specified label. I believe most of this can be removed if you have the user provide F.set field
directly rather than provide it automatically for them.
@thomashoneyman I see an invitation from you, but it gives me a 404 when I try to go to the repo. Is this the right repo name? https://github.com/thomashoneyman/purescript-agnostic-counter
It's https://github.com/thomashoneyman/purescript-agnostic
. Let me try again.
I can see that repo now, thanks!
@ajnsit I've had some time away from the agnostic backend experiment, and at this point I think it's simply too complex to maintain over time especially as UI libraries continue to diverge (I'm here thinking of React Hooks) and it becomes harder to translate patterns from one library into those used by another. As we were able to demonstrate there, it's possible to create framework-agnostic components, but there are two main issues:
I think that library-agnostic components are very much possible to write, but that they're a better fit for application code rather than library code. It simply makes the library too complicated, to the point where I'd expect maintenance to become a serious issue over time.
I'd like to write up the results of the experiment and share them so others can have a go at the same topic -- including a formless-independent
, but I don't think that I'll be the one to pull it off at this point.
I'm going to close this issue because I don't plan to release a formless-independent
at this time. However, feel free to continue the discussion here.
While porting the library to the Concur UI Framework, I realised that most of the important bits are completely independent of Halogen. So I went ahead and extracted formless completely and created Formless-independent.
I think it might be valuable to have an official package called
formless-core
orformless-independent
for this, and then having framework specific bits in their own packages such asformless-halogen
orformless-concur
, which would in turn depend on the core library.What do you think?