Chris Done on a new pure Haskell pgsql binding and MVC

None of us should miss this. It especially raised a few questions in my head about the architecture of my site. http://chrisdone.com/posts/2011-06-05-postgres-hpaste-edsls.html

I am all for increasing the M in the MVC of Yesod. In a simple REST app
where resources closely map to the data modeling, this can be less of an
issue. Basically you need the M once you would have duplicate model code
between any 2 requests. Chris is advocating a fairly strict separation. I
think it can still be simpler to put a read query in a controller. Once that
query (or a part of it) is needed elsewhere it should definitely be named
and reused. Updates do need to maintain invariants and thus should generally
stay out of the controller. I would like to come up with a good system for
maintaining invariants and propagating violations to forms, and other useful
systems for doing more than just taking the query out of the controller.
Let us know if there is anything stopping you from separating out code into
a Models directory right now.
Greg Weber
On Fri, Jun 10, 2011 at 4:00 AM, Aur Saraf
None of us should miss this. It especially raised a few questions in my head about the architecture of my site.
http://chrisdone.com/posts/2011-06-05-postgres-hpaste-edsls.html
_______________________________________________ web-devel mailing list web-devel@haskell.org http://www.haskell.org/mailman/listinfo/web-devel

On 10 June 2011 16:12, Greg Weber
I would like to come up with a good system for maintaining invariants and propagating violations to forms, and other useful systems for doing more than just taking the query out of the controller. Let us know if there is anything stopping you from separating out code into a Models directory right now.
An example of managing invariants and propagating violations to forms?

Hi Chris, I like the new url generator package, which should also be useful in focusing an application around the models. In Yesod one would currently check a form for errors and if there aren't any then insert the model, and this is all done from the controller. This puts model logic into the form. We did have techniques (not sure what the state of it is at the moment) to declare fields as required in the schema, and to have that automatically reflected in a form for a model. But a model may have more complex logic than that- a field may be required based on the value of another. This could be handled by indicating a validation function in the schema rather than just a boolean as to whether it should be required. This would also help handle more complex validations than whether just a field is required. One idea behind putting this in the schema, is that the logic is separate from a form. However, the normal way of creating a new model is just a simple insert. insert User { name = "Chris", email = "e", requiredCheckbox = False } So if that is done, but not first from a form (an API perhaps), we have nothing to validate our model. In Rails, in my model file, I can declare validations of arbitrary complexity. To save a model, I have to call model.save, which will run any validations and any callbacks in general (There could be a before_create callback that automatically sends an e-mail). So we could start in Yesod by having a convention of calling save User { name = "Chris", email = "e", requiredCheckbox = False } which would run validations and return a boolean. However, we wouldn't have our list of errors from that. In Rails, calling save would return a boolean, but also have the side effect of either setting the model id or creating an errors variable containing a list of errors that you can inspect and that the form can use. We could return instead return an Either, with the alternative result of the key (id) or the errors for the model. And ideally those errors could be propagated back to a form. Rails also has save! which will throw an exception if the model is not saved- this gets around having to check for errors if you believe there won't be any. There is a data storage library on hackage that supports triggers. http://hackage.haskell.org/packages/archive/TCache/0.8.0.2/doc/html/Data-TCa... One can emulate trigger callback behavior by writing their own save function, however the problem is that 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 something like TCache where triggers are tied into the persistence layer.
From my limited Ruby experience, it is the triggers and validations that one attaches to the models, that really define the business logic of the model, (lets ignore for now how one deals with relationships to other models). Much of the rest of putting code into models is about having a nicer way to share, organize, and test code.
Greg Weber
On Mon, Jun 20, 2011 at 4:07 AM, Christopher Done
On 10 June 2011 16:12, Greg Weber
wrote: I would like to come up with a good system for maintaining invariants and propagating violations to forms, and other useful systems for doing more than just taking the query out of the controller. Let us know if there is anything stopping you from separating out code into a Models directory right now.
An example of managing invariants and propagating violations to forms?

On 20 June 2011 17:34, Greg Weber
I like the new url generator package, which should also be useful in focusing an application around the models.
I'd think it's more to do with the controller/view. E.g., I can have multiple URLs to the same resource via HTML/JSON, or whatever. How does it relate to models?
In Yesod one would currently check a form for errors and if there aren't any then insert the model, and this is all done from the controller. This puts model logic into the form.
Yeah, this sucks a bit. I'm a big supporter of having the model enforcing consistency. I quite like domains and constraints in PostgreSQL for this reason.
We did have techniques (not sure what the state of it is at the moment) to declare fields as required in the schema, and to have that automatically reflected in a form for a model. But a model may have more complex logic than that- a field may be required based on the value of another. This could be handled by indicating a validation function in the schema rather than just a boolean as to whether it should be required. This would also help handle more complex validations than whether just a field is required. One idea behind putting this in the schema, is that the logic is separate from a form. However, the normal way of creating a new model is just a simple insert.
insert User { name = "Chris", email = "e", requiredCheckbox = False }
In your application it's hard to say whether this is a good idea. Something to do with the required checkbox certainly shouldn't be in the model. What if you're interfacing with the application via a JSON or XML interface that has no concept of checkboxes or the notion of a user-choice in this area? It's worth establishing what we mean by MVC otherwise we're discussing the taste of ham and hamsters. I think the definition of the model is to handle and enforce business logic to do with application state that would still exist if you completely removed the view and controller. The model only works on valid input, and, where impossible to enforce with the type system, should barf on bad input. Stuff like getting input from the web request (like a form's inputs), caching pages, redirecting, choosing the right view to display and the right model objects to call depending on the input seems like the controller's job. Whether the controller is getting input from JSON, XML, form inputs, parsing files, etc. it should validate its input and give the model what it wants. The view provides a format with which the controller can send a representation of the model and receive commands; could be a curses UI, a web page, a SOAP API, an RSS feed, etc. Some MVC in practice will have the view call the model directly for what it needs which is convenient but, in my current view, is inversing control in some bad way. In this way, code that should be in the model ends up being put in the view, and the model ends up living to serve the needs of a particular view. I'm not sure whether your view actually differs to this, or whether we're just unclear.
From my limited Ruby experience, it is the triggers and validations that one attaches to the models, that really define the business logic of the model, (lets ignore for now how one deals with relationships to other models). Much of the rest of putting code into models is about having a nicer way to share, organize, and test code.
It seems a pain to expose the actual schema to the rest of the application. Shouldn't you just have generic methods that abstract the concept of creating database entries, reading files, etc. to do with the state? It seems easier to take the "big walls and gate" approach. Paris of Troy, being the cheeky lad that he was, took Helen from her husband, Menelaus. I don't know what Menelaus was doing at the time. Probably tending to issues of agriculture, directing his masses of armies, being the king of Sparta, that lark (I think we was really down the pub). Despite supposedly being great at logistics, he seemed to have poor people skills, because he sought to exact revenge not on only Paris, but the whole of Troy, by having a big fight. A proper war. The Spartans in general seemed to have a bad attitude. Helen of Sparta was said to be the most beautiful of all women. Not just really beautiful or the best pair of legs this side of Greece, but the most beautiful of "all" women. This makes one wonder who started that rumour; my money's on Helen. Since he didn't know his arse from his shoes, losing wives left, right and center, Menelaus hired his brother, Agamemnon who was more than happy to sort it out. Troy was designed to be a nice city, and it was. At first, armies would come from across Greece and kick the crap out of everything, stealing things, etc. The Trojans didn't like that. Later, they let people in, but installed guards that would wait around for things to kick off, and spring to action when things went bad. Sadly, this only worked so far, as people were killed and stalls smashed up, and sly graffiti artists would write “Paris is φαλλός” on walls when the guards weren't paying attention. They found it hard to separate the nice city and city people from the riffraf. Finally, they decided to erect a big wall around the city, to keep it nice. With one big gate at the front. Nobody could get in without first being inspected, checked. Otherwise they were told to sling their hook. At this point Agamemnon decided to attack. Pretty bad time to try to besiege a city that had recently had a brand new wall installed. Anyway, long-story-short the Trojans were idiots because they let a huge horse-shaped box containing person-shaped killing machines into the city without inspecting it at all on the outside of the wall or the inside. Subsequently the gate was compromised and a load of bad stuff went down. Bad Troy. I despair! Got a bit carried away there…

Yes, I think our understanding of MVC is the same, as is pretty much
everyone's when it comes to web apps. The only real difference is what code
they write in practice- and I am still unclear as to what your plans are
there.
On Mon, Jun 20, 2011 at 12:03 PM, Christopher Done wrote: t seems a pain to expose the actual schema to the rest of the
application. Shouldn't you just have generic methods that abstract
the concept of creating database entries, reading files, etc. to do
with the state?
participants (3)
-
Aur Saraf
-
Christopher Done
-
Greg Weber