Closed seanhess closed 1 month ago
Interesting! I wonder if it can be made extra ergonomic with something like higgledy:
data User
= User
{ name :: String
, age :: Int
, ...
}
deriving Generic
-- HKD for free!
type UserF f = HKD User f
Could this enable skipping the HKD altogether and just having a form for the data structure directly?
Neat, I didn't know about higgledy! I like the idea
Could this enable skipping the HKD altogether and just having a form for the data structure directly?
Take a look at Example.Forms. With the current approach, the user has to deal with the higher kinded type directly to do validation: (UserForm Validated
in the example). Would it be more or less confusing for them to create a type with Higgledy, but then need to use UserF Validated? Maybe with clever conventions it becomes more clear:
data User
= User
{ name :: String
, age :: Int
, ...
}
deriving Generic
-- name this "XForm" instead of XF
type UserForm f = HKD User f
-- they still have to create one manually
validateForm :: User -> UserForm Validated
validateForm u =
UserForm
{ user = validateUser u.user
, age = validateAge u.age
, pass1 = validatePass u.pass1 u.pass2
, pass2 = NotInvalid
}
-- user needs to generate a "FormField" version of their form data structure to power the field funciton
formView :: UserForm Validated -> View FormView ()
formView v = do
--
let f = genFieldsFrom v :: UserForm FormField
...
Update: I don't think Higgledy can work. There's no direct UserForm
constructor, they use fancier composable constructors, at the cost of approachability and clarity. In the above example the user constructs a UserForm Validated
using record-syntax. I think getting people more familiar with the pattern by asking them to create the type directly is easier and more clear.
If we're getting into the weeds of composable types, I'd rather drop this PR and go back to type-addressed fields like in the main branch.
Merged, made some simplifications, but this approach is definitely going to be more scalable and intuitive than the type-per-field approach
This PR changes hyperbole to use higher-kinded records for forms. Is this easier to use / better?
In the current (old) form example, we use newtypes to differentiate form fields:
We replace these with the following
We can now use the same shape for different data.
UserForm Identity
refers to the form data, while aUserForm Validated
constains a bunch ofValidated a
.Forms Interface
Changes from the old version, where the validated object is threaded through the form's context and type indexed:
Or with this PR we address the field with dot-syntax. Note that we have to create a
UserForm (FormField Validated)
first. (An empty one can be generated withgenFields
)Advantages of this new approach
form
: you pass what you need directly to fieldsDisadvantages of this approach
Any thoughts?