<html><body style="word-wrap: break-word; -webkit-nbsp-mode: space; -webkit-line-break: after-white-space; "><br><div><div>On Jan 24, 2011, at 11:54 PM, Michael Snoyman wrote:</div><br><blockquote type="cite"><div>* They are writing a site that involves very few of the features<br>offered by a framework. For example, no routing, no persistence, no<br>templating. In such a case, it's likely that a framework will simply<br>get in their way.<br></div></blockquote><div><br></div><div>Yes. I think that is a pretty small subset of web applications?</div><br><blockquote type="cite"><div>* Haskellers tend to be an "opinionated" bunch, so it's not surprising<br>that any random Haskeller may disagree with the design decisions of<br>specific frameworks.<br></div></blockquote><div><br></div><div>I think that many random Haskellers start by assuming their ideas are better than everyone else's and don't even look to see if they disagree with the decisions of specific frameworks :p</div><div><br></div><blockquote type="cite"><div>Can you honestly tell me that you think Happstack is that right tool<br>for every site? </div></blockquote><div><br></div><div>Not for every site. But possibly for every site that Neil is going to be working on. Many of his 'complaints' about WAI are things which are addressed by Happstack to some degree:</div><div><br></div><div>1. statusOK vs status200 - happstack offers combinators like, ok, notFound, seeOther, unauthorized, etc. and a function 'resp' for setting the response code explicitly.</div><div><br></div><div>&nbsp;2. predefined list of headers like "content-type" - happstack includes many constants an Handler.hs (they could be exposed better though)</div><div><br></div><div>&nbsp;3. what happens when using 'ResponseFile' but the file is not found - happstack's documentation says what happens when serveFile or serveDirectory does not find the file</div><div><br></div><div>&nbsp;4. exception handling -- the happstack docs show how to integrate exception catching and handling into your ServerPartT (though they could be more explicit about what happens to uncatch exceptions)</div><div><br></div><div>&nbsp;5. happstack-server currently supports lazy IO natively and should be a good match&nbsp;</div><div><br></div><div>&nbsp;6. happstack has a showResponse function for debugging</div><div><br></div><div>&nbsp;7. the ServerPartT monad has a built-in FilterT monad which makes it easy to apply a function (Response -&gt; Response) to modify the response.&nbsp;</div><div><br></div><div>&nbsp;8. killing the running background threads in GHCi is really tricky. You need to basically track all the forkIO calls and save the threadIds so that you can come back and kill them &nbsp;later when you get Crtl+C. But, happstack does have support for doing that :) Also, the waitForTermination function that happstack provides also has support for Windows.</div><div><br></div><div>So, given the fact that all the points Neil raised regarding WAI are addressed to some degree already in Happstack -- it seems like working with Happstack would not be a bad thing..</div><div><br></div><blockquote type="cite"><div>That's one of the main reasons I've designed Yesod and WAI the way I<br>have. By splitting the project up into so many pieces (Hamlet,<br>Persistent, authenticate, etc), users get to pick and choose exactly<br>what they want.</div></blockquote><div><br></div><div>Yes. Happstack is the same way.</div><div><br></div><blockquote type="cite"><div>It's very likely that Neil may end up reinventing a lot of the<br>features of a framework in this Hoogle project. But I *still* think<br>this is a good thing:<br></div></blockquote><div><br></div><div>Based on my familiarity with Neil's previous work on things like catch, super-o, and other projects, I am interested to see what he comes up with as well. He has a tendency to address important issues that other people have swept under the rug.&nbsp;</div><div><br></div><blockquote type="cite"><div>* And even if none of that happens, it probably took him less time to<br>rewrite the features he needed than go through either the Happstack or<br>Yesod tutorials. For someone developing lots of sites, the learning<br>curve is IMO worth it; for a one-off project, I doubt it is.<br></div></blockquote><div><br></div><div>I highly doubt that. It can't take more than an hour or two to go through the happstack tutorial. What I do think is true is that:</div><div><br></div><div>&nbsp;1. writing code is satisfying and feels productive</div><div>&nbsp;2. reading documentation is boring and doesn't feel productive</div><div>&nbsp;3. most programmers assume their code is like a &nbsp;beautiful flower, and that everyone else's is a putrid swamp of sewage.</div><div><br></div><div>And hence they are happy to spend days reinventing something rather than spend an hour reading about an existing solution :p (This is not directed at Neil who actually asked for advice about what already existed. Though he may still feel that Happstack is a putrid swamp of sewage...)</div><div><br></div><div><blockquote type="cite"><div>* If he is so inclined, he could release this "boilerplate" code into<br>a separate package and, walla, we have a *new* Haskell framework,<br>building on top of what we have already, that caters to a new<br>audience.<br></div></blockquote></div><div><br></div><div>Or, maybe it just reimplements what we already have with minor cosmetic differences, weaknesses compared to other frameworks due to have less real world usage, and a few improvements which could have been more easily added to existing frameworks.</div><div><br></div><div>For example, let's consider what a framework is likely going to have for it's monad. For starters you are going to want easy access to the Request. So you'll have something like this:</div><div><br></div><div>&gt; type FrameworkT = ReaderT Request m a</div><div><br></div><div>Then you'll realize you need some way to escape things early and return an error, so you might add the ErrorT monad transformer so you have a way to escape and return a Response.</div><div><br></div><div>&gt; type FrameworkT = ReaderT Request (ErrorT Response m) a</div><div><br></div><div>Then you'll realize you need some way to modify the Responses to do things like add extra headers (for cookies, etc) or compress the Response, or whatever. So you might had a StateT that can hold filters to apply:</div><div><br></div><div>&gt; type FrameworkT = ReaderT Request (ErrorT Response (StateT (Response -&gt; Response) m)) a</div><div>&gt;</div><div>&gt; setFilter :: (Response -&gt; Response) -&gt; FrameworkT m a</div><div>&gt; appendFilter :: (Response -&gt; Response) -&gt; FrameworkT m a</div><div><br></div><div>And then you might decide that you want your FrameworkT monad to be an instance of MonadPlus, so you add in MaybeT:</div><div><br></div><div>&gt; type FrameworkT = ReaderT Request (ErrorT Response (StateT (Response -&gt; Response) (MaybeT m))) a</div><div><br></div><div>And now you have created a new Framework monad that looks suspiciously like the ones that already exist in other frameworks. Happstack, Snap, and Yesod all have variations on this basic theme.&nbsp;For example, in Yesod it appears you ultimately came up with,</div><div><br></div><div><meta charset="utf-8"><span class="Apple-style-span" style="font-family: Times; "><pre><span class="hs-keyword" style="color: blue; ">type</span> <span class="hs-conid">GHInner</span> <span class="hs-varid">s</span> <span class="hs-varid">m</span> <span class="hs-keyglyph" style="color: red; ">=</span>
<a name="line-216"></a>    <span class="hs-conid">ReaderT</span> <span class="hs-layout" style="color: red; ">(</span><span class="hs-conid">HandlerData</span> <span class="hs-varid">s</span> <span class="hs-varid">m</span><span class="hs-layout" style="color: red; ">)</span> <span class="hs-layout" style="color: red; ">(</span>
<a name="line-217"></a>    <span class="hs-conid">MEitherT</span> <span class="hs-conid">HandlerContents</span> <span class="hs-layout" style="color: red; ">(</span>
<a name="line-218"></a>    <span class="hs-conid">WriterT</span> <span class="hs-layout" style="color: red; ">(</span><span class="hs-conid">Endo</span> <span class="hs-keyglyph" style="color: red; ">[</span><span class="hs-conid">Header</span><span class="hs-keyglyph" style="color: red; ">]</span><span class="hs-layout" style="color: red; ">)</span> <span class="hs-layout" style="color: red; ">(</span>
<a name="line-219"></a>    <span class="hs-conid">StateT</span> <span class="hs-conid">SessionMap</span> <span class="hs-layout" style="color: red; ">(</span> <span class="hs-comment" style="color: green; ">-- session</span>
<a name="line-220"></a>    <span class="hs-conid">IO</span>
<a name="line-221"></a>    <span class="hs-layout" style="color: red; ">)</span><span class="hs-layout" style="color: red; ">)</span><span class="hs-layout" style="color: red; ">)</span><span class="hs-layout" style="color: red; ">)</span></pre></span></div>:p&nbsp;</div><div><br></div><div>We don't really need anyone to reinvent another variation of this out of ignorance. They should either blatantly rip it off, or tell us why it is wrong and come up with something better :p</div><div><br><blockquote type="cite"><div>I don't like seeing a fractured community, but I think this is exactly<br>the opposite effect: we're seeing new levels of integration of<br>pre-existing tools than we saw before.<br></div></blockquote></div><br><div>Oh ?&nbsp;</div><div><br></div><div>Anyway, I am not against the development of new frameworks. But I think it would be better if people started new frameworks because they have discovered why the current ones are fundamentally broken, rather than just assuming they are and then reinventing the same thing :) But, in practice, people are going to keep reinventing the wheel, so I'll just keep my eye on them and steal the good parts ;)</div><div><br></div><div>- jeremy</div><div><br></div><div>p.s. Nothing in this message is intended to be a personal attack on anyone specific. It more reflects years of hearing people say, "Oh I didn't realize happstack already could do that."&nbsp;</div><div><br></div></body></html>