
Yesod (and AFAIK other haskell frameworks) do not have strong models. In my experience, the heart of an individual model is validation logic. One can achieve validation by writing their own save function, however this allows multiple possible entries into saving a model because they can write multiple different save functions. Real enforcement of a single entry point with triggers is possible in TCache [1] where triggers are tied into the persistence layer. The main other issue I see causing weak models is the separation of forms from models. So I want to propose a design for stronger models and see what critiques and better ideas there are. Lets start with a data structure for a model: Person { personAge :: Int, personSex :: Maybe String } The main aspect is the addition of triggers, which are callbacks ran before a save to the database. A trigger is either a validator or a modifier. A validator is run as a callback before a save to the database. myTrigger :: Person -> Either ValidateError Person A ValidationError will be associated with a particular model, and possibly with a particular field of that model. We may want a validation errors to stop any more validations from being ran, or we may want to run all validations (just as a form returns all errors, not just one). ValidationError = FieldInvalid EntityField Text | BaseInvalid Text | ShortCircuitValidation ValidationError verifySex :: Person -> Either ValidationError Person verifySex p = when ((personAge p) > 18 && isNothing (personSex p)) FieldInvalid PersonSex "is required" return p Note that this is a non-monadic trigger, but triggers do need the ability to run database queries. One approach to registering callbacks would be to place them in the schema. Person triggers=verifySex, trigger2, trigger3 age Int sex String Maybe So now we can have a save function. ModelSave val = ModelValid val | ModelInvalid val [ValidationError] save :: val -> ModelSave val sav <- savePerson person case sav of ModelValid person -> ModelInvalid (person, errors) -> Note that both Constructors have a person. This is to allow triggers to modify the person. Now we need a technique for tieing the model to the form. I will use some magical functions. getNewPerson = do f <- newPersonForm [|hamlet| ^{f} |] postCreatePerson = do res <- saveForm personForm case res of ModelValid person -> ... ModelInvalid (person, errors) -> editPersonFrom person errors [1] http://hackage.haskell.org/packages/archive/TCache/0.8.0.2/doc/html/Data-TCa...