
Georgi, your're absolutely right and I hope that I was clear enough when stating that Grace *does* have dynamic forms, but with the limitations stated in the project README. In fact I shall study how they are realized under the hood. Noon is right in that one should be careful not to rely on the hippest JavaScript framework which will become obsolete soon. I've experiemented a little and conclude that monads provide a suitable abstraction for dynamic forms, because >>= facilitates generation of form parts conditional on results of other form evaluations. A proof-of-concept with InputT IO as the monad is published at https://hub.darcs.net/olf/GenericForm/browse/GenericForm.hs In its current state, it is capable of generating forms for types such as Either String (Int,Bool). I also realized that while Yesod's MForm is a monad (transformer), running a form produces a list of FieldView values, which the programmer must assemble into the actual form html. While hiding some fields conditionally may be feasible with yesod-form, I think forms for recursive types are out of the question. As I understand it, the monadic structure of MForm means that field values are *evaluated* conditionally, but the fields must be present statically. Maybe Michael Snoyman can weigh in on this. Olaf On Fri, 2023-03-03 at 09:58 +0200, Georgi Lyubenov wrote:
Hey
With the risk of being annoying, I want to point out that the Grace browser does indeed somewhat support your original use case, because I feel like it might not be something obvious (there also is not an example for it, I think).
With the term ``` merge { Number: \n -> "The number is " + Real/show n , Check: \b -> if b then "checked" else "unchecked" } ``` which is equivalent to pattern matching on the input and picking a case, we get html with two radio buttons: * Check - when this is selected, the output is based on whether a checkbox is checked * Number - when this is selected, the output is based on the value in an input text field.
Of course, this is still suboptimal - the input text field and checkbox are shown at all times, but I feel like I would have misrepresented the language if I did not point out it does support something like this.
Cheers, Georgi
On 3/2/23 17:01, Olaf Klinke wrote:
On Thu, 2023-03-02 at 15:51 +0200, Georgi Lyubenov wrote:
Hey Olaf,
This is not an answer to your question, but I was reminded of Grace[0], which is a language with a "browser"[1] that allows you to type in terms and get back webpages based on those terms "automagically", which sounds like exactly what you need. I don't know how it's implemented, so I don't know if it is actually relevant to you, but it is worth noting that Grace itself is implemented in Haskell.
Cheers, Georgi
[0] https://github.com/Gabriella439/grace [1] https://trygrace.dev/
On 3/2/23 12:54, Olaf Klinke wrote:
Dear Cafe,
has anyone ever attempted (and maybe succeeded) in building dynamic forms using one of the Haskell web frameworks (Yesod,Servant,...)? By "dynamic" form I mean a form that changes the number of fields based on selections the user makes in other fields of the form.
For example, say one has an algebraic data type
data T = Number Int | Check Bool T
A form for such a type would initially consist of radio buttons or a drop-down list with options "Number" and "Check" that lets the user select the constructor. When "Number" is selected, an <input type="number"> field is shown. When "Check" is selected, an <input type="checkbox"> is displayed next to another form of this kind.
In the end, one would use the GHC.Generics machinery to generate forms for a wide range of algebraic data types. I've seen this in the Clean language's iTask library [1] and it's very convenient. Of course this would involve a lot of JavaScript like document.createElement() as well as book-keeping how to re-asseble the fields into a T value upon submission. At least the latter is already handled by libraries such as yesod-form.
Olaf
[1] https://cloogle.org/src/#itasks/iTasks/UI/Editor/Generic [2] https://github.com/haskell-servant/servant-swagger/issues/80
Thanks for the pointer!
The Grace README says under Notable Omissions:
Recursion or recursive data types
Grace only supports two built-in recursive types, which are List and JSON, but does not support user-defined recursion or anonymous recursion.
User-defined datatypes
All data types in Grace are anonymous (e.g. anonymous records and anonymous unions), and there is no concept of a data declaration The tutorial shows how Grace function inputs are mapped to forms, where functions with List input indeed have a form that is "dynamic" in the sense I defined. Otherwise there is only one input field per function argument. That means complex types are to be input in JSON syntax and parsed. Instead of a DSL, I'd prefer a shallow embedding into Haskell, so that one can leverage all the available machinery. Yet Grace already goes a long way towards what I am after.
Olaf