<div>Yesod (and AFAIK other haskell frameworks) do not have strong models. In my experience, the heart of an individual model is validation logic.</div><div><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">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. </span></div>

<div><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">Real enforcement of a single entry point with triggers is possible in TCache [1] where triggers are tied into the persistence layer.</span></div>

<div><span class="Apple-style-span" style="border-collapse: collapse; font-family: arial, sans-serif; font-size: 13px; ">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.</span></div>

<meta http-equiv="content-type" content="text/html; charset=utf-8"><div><br></div><div>Lets start with a data structure for a model:</div><div><div><br></div><div>Person { personAge :: Int, personSex :: Maybe String }</div>

</div><div><br></div><div>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.</div><div>A validator is run as a callback before a save to the database.</div>

<div><div><br></div><div>   myTrigger :: Person -&gt; Either ValidateError Person</div></div><div><br></div><div>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).</div>

<div><br></div><div>ValidationError = FieldInvalid EntityField Text</div><div>                       | BaseInvalid Text</div><div>                       | ShortCircuitValidation ValidationError</div><div><br></div><div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div>

verifySex :: Person -&gt; Either ValidationError Person</div><div>verifySex p =</div><div>  when ((personAge p) &gt; 18 &amp;&amp; isNothing (personSex p)) FieldInvalid PersonSex &quot;is required&quot;</div><div>  return p</div>

</div><div><br></div><div><meta http-equiv="content-type" content="text/html; charset=utf-8"><div>Note that this is a non-monadic trigger, but triggers do need the ability to run database queries.</div></div><div><div>One approach to registering callbacks would be to place them in the schema.</div>

</div><div><br></div><div><div>Person triggers=verifySex, trigger2, trigger3</div><div>  age Int</div><div>  sex String Maybe</div><div><br></div><div>So now we can have a save function.</div><div><br></div><div>ModelSave val = ModelValid val | ModelInvalid val [ValidationError]</div>

<div>save :: val -&gt; ModelSave val</div><div><br></div><div>sav &lt;- savePerson person</div><div>case sav of</div><div>  ModelValid person -&gt;</div><div>  ModelInvalid (person, errors) -&gt;</div><div><br></div><div>

Note that both Constructors have a person. This is to allow triggers to modify the person.</div><div>Now we need a technique for tieing the model to the form. I will use some magical functions.</div><div><br></div><div>getNewPerson = do</div>

<div>  f &lt;- newPersonForm</div><div>  [|hamlet| ^{f} |]</div><div><br></div><div>postCreatePerson = do</div><div>  res &lt;- saveForm personForm</div><div>  case res of</div><div>    ModelValid person -&gt; ...</div><div>

    ModelInvalid (person, errors) -&gt; editPersonFrom person errors</div><div><br></div><div><br></div><div>[1] <a href="http://hackage.haskell.org/packages/archive/TCache/0.8.0.2/doc/html/Data-TCache.html">http://hackage.haskell.org/packages/archive/TCache/0.8.0.2/doc/html/Data-TCache.html</a></div>

</div>