

Snap: A Haskell Web Framework - tomh-
http://snapframework.com/

======
tumult
This looks great. If you are a non-Haskeller or only a semi-Haskeller and are
wondering what technical features set this apart from other existing
frameworks:

 _The Snap framework uses a style of I/O called “iteratee I/O”. We have opted
for iteratees over handle-based and lazy I/O because iteratee I/O offers
better resource management, error reporting, and composability._

If you're interested, you can read some in-depth stuff here:
<http://okmij.org/ftp/Streams.html> (these papers are somewhat famous)

The Snap tutorial has a decent summary of how they work, but it doesn't
provide a lot of context for why.

If you are already familiar with functional programming, then you know about
foldl, foldr, and left/right recursion. When processing a collection of some
kind (like a list), usually you take an enumerator (the fold function or a map
function, for example) and pass it an iteratee (the function that will be
applied to each element) and then your work gets done and you get a result.
Left fold is non-lazy, right fold and map are lazy.

Because Haskell is a lazy language, there is another way to process lists
lazily: with explicit recursion _to the right_. If you are familiar with
Scheme or Lisp, it would look kind of like

    
    
        (define (myfunc xs)
          (cond ((null? xs) '())
                 (else (cons (+ (car xs) 1)
                             (myfunc (cdr xs))))))
    

which would add 1 to each element of a list, returning a new list.

In Lisp or a non-lazy Scheme, this would blow up in your face for a large
list. In Haskell it's no sweat, because the list will only be processed as it
is needed, and generally for control structures you have to go out of your way
to make it 'space leak' (build up a bunch of unevaluated expressions and then
explode when you finally evaluate it.)

If you use foldr or map in Haskell, it works like this. One interesting thing
about using non-explicit recursion in Haskell is that if you put a bunch of
maps or folds (or with the Stream Fusion library, many different types of list
operations) next to each other, the compiler will inline them together and
eliminate intermediate list allocations. So you can end up writing your
process as a series of discrete logical steps (from list type a, to b, to c,
to d) but still get the performance of doing only one traversal. Very nice.

One of the downsides is that you end up with a lazy control structure, which
can be problematic when dealing with the real world. For example, when reading
from a file handle, someone could move the file or delete it or something.
With lazy IO, the programmer is not really controlling _when_ the file will be
read by the program. Often, this is not a problem. However, with networking
and time-critical applications, it can be a big problem.

Another problem is that if you need to perform allocations (like going from a
list of one length to a list of another length) in the middle of your
transformations, it gets really tricky if you want to maintain fusion.
Normally with explicit right recursion you can just cons together a larger
list on the fly, but we can't do fusion on explicit recursion, and compilers
are generally bad about figuring out what kinds of allocation our explicitly
recursing function does.

We don't want to give up our elegant way of expressing operations of
sequences, but we want to know when we are writing and reading from the real
world, and we want to do it with determinate constant space. Iteratees let us
do that almost as elegantly as the normal list stuff by driving the consuming
of the list via the functions that do the actual processing. Sort of like a
list unfold, but not quite.

Check out attoparsec-iteratee <http://hackage.haskell.org/package/attoparsec-
iteratee> for an example of iteratee usage: it turns a fast bytestring parser
into one that's based on iteratees, which will probably end up being even
faster. (correction: which will allow you to process streams in constant space
without needing to explicitly manage things.)

(If Dons or Gregory Collins is around to correct the numerous mistakes I'm
sure I made in this post, please do so!)

~~~
how_gauche
> Check out attoparsec-iteratee
> <http://hackage.haskell.org/package/attoparsec-iteratee> for an example of
> iteratee usage: it turns a fast bytestring parser into one that's based on
> iteratees, which will probably end up being even faster.

Iteratees don't really make things "faster" (you end up with a sequence of
imperative read()/write() calls just like everything else), but they allow you
to stream in O(1) space while still being easily __composable __.

For example, we do gzip, buffering, & chunked transfer encoding using simple
function calls, and we don't really have to worry about explicit buffer
management or control flow while we're doing it.

~~~
tumult
Fixed, thanks!

------
Legion
I don't write Haskell, but the documentation on that page almost made me wish
I did.

Other projects, take notes.

~~~
tomh-
This kind of documentation is rare for Haskell projects, many other attempts
failed silently gaining traction because documentation writing is usually the
last item on the todo list for haskell projects, really glad this one took
such an effort for it!

~~~
dons
> documentation writing is usually the last item on the todo list for haskell
> projects

That's unfair. Documentation writing is usually the last item for software
developers. Full stop.

~~~
tomh-
Yes you are right, it was unfair to say that its because it is the last todo
item for Haskell projects. However I feel that it is even more important for
Haskell projects to actually do that last item. I've seen several webframework
projects like hApps, Happstack, turbinado which are probably great pieces of
software but hard to get started on. Personally for me it is a lot harder to
start doing Haskell web development coming from a background of MVC based web
development. Even though I know Haskell better than python or ruby, it is
easier for me to pick up python or ruby frameworks because the way I think in
writing web applications (which is mainly imperative). This is where great
documentation comes into play. Snap framework seems to offer this transition
easier, so will definitely take a peak at it!

------
guano___
The example for the echohandler is wrong, to solve, replace

    
    
        req <- getRequest
        writeBS $ maybe "" id (getParam "s" req)
    

with

    
    
        param <- getParam "s"
        writeBS $ maybe "" id param

------
catch404
havn't yet looked at Haskell much, but I had a look at the quick start and
it's really well written. The website is one of the cleanest I've seen for a
framework in a while, well done on the design!

------
alrex021
I'd love to see mustache <http://mustache.github.com/> ported over to Haskell
now. (Maybe this would make a good pet project.)

~~~
jimmyjazz14
I started working on something somewhat similar to mustache for haskell a
while back, though its still in the early alpha stage of development.
<http://github.com/jamessanders/rstemplate>

------
ozataman
Amazing documentation right from the start. Kudos to all the members of the
project!

------
crazydiamond
I had a look at the benchmarks. Could someone confirm -- larger values (bars)
means better. So RoR is the worst, and Snap the best on both benchmarks.

~~~
chriseidhof
Yes, it's the number of requests handled. More requests served is better.

~~~
acangiano
What was the deployment stack like for Rails? The numbers are way off.

~~~
mightybyte
I downloaded Rails, followed a few online tutorials to build the app, and ran
script/server to run the server. As mentioned in the benchmarking document,
we're not experts on Rails deployment and will gladly accept suggestions for
improvements that can be implemented in a few minutes.

~~~
acangiano
You are running the benchmark with WEBrick, a slow, development only web
server. For Rails you should run nginx + Passenger.

~~~
davidmathers
_For Rails you should run nginx + Passenger_

No, that's for a production website and requires setup. They just need to
replace webrick with thin.

> gem install thin

then

> thin start

instead of

> ./script/server

Simple.

~~~
rufugee
Um...shouldn't it also be:

script/server -e production

Running in development mode severely hampers Rails' performance.

~~~
davidmathers
Good point. I also noticed they're running snap with 4 threads. Since rails is
multi-process rather than multi-thread they would actually need to set up
something like passenger as acangiano suggested.

Better would be to just limit the test to single-process/single-thread. The
idea should be to compare web frameworks not web servers.

~~~
mightybyte
Yes, but it's also useful to compare similar levels of effort. Using 4 threads
with Snap is a similar level of effort to using thin. Setting up 4 separate
processes with passenger requires quite a bit more effort, especially if you
haven't done it before.

~~~
davidmathers
You can set up 4 processes with thin, just do:

> thin -e production -s 4 start

The problem is that each process listens on its own port. They can't all
listen on port 3000, so you need some sort of proxy to parcel out the
requests.

------
whalesalad
"An XML-based templating system for generating HTML" That turned me off
immediately.

~~~
how_gauche
Sorry you feel that way, at least it's optional. The idea is that we are
interesting in doing DOM-level transformations bound to html tags and there
just isn't a robust HTML5 DOM parser available for Haskell.

~~~
jamesbritt
What would you suggest for "populate placeholders in a string and send it out"
page rendering?

~~~
how_gauche
I personally like <http://hackage.haskell.org/package/HStringTemplate> for
that.

~~~
jamesbritt
Thanks!

------
klaut
i am not sure if it was supposed to be like this, but all i get when i visit
the url, is a list of random scribd links. And the links have nothing to do
with Haskell.

~~~
dons
That makes no sense.

~~~
klaut
this is what i get (i just did a screengrab): <http://twitpic.com/1px4b8>

I am confused as well.

~~~
dons
You're searching with google. DNS issue, and browser confused?

~~~
klaut
No, I did not search with google. I clicked directly on the link provided
here.

~~~
dons
The result is a google search page.

~~~
nostrademons
That's not a regular Google search page - possibly a custom search engine, but
regular Google looks very different.

------
elbenshira
Now, if only I can wrap my head around freaking monads.

~~~
die_sekte
Monads are pretty simple:

Think of a monad as a spacesuite full of nuclear waste in the ocean next to a
container of apples. now, you can't put oranges in the space suite or the
nucelar waste falls in the ocean, _but_ the apples are carried around anyway,
and you just take what you need.
(<http://koweycode.blogspot.com/2007/01/think-of-monad.html>)

That explanation is pretty good if you understand monads. Otherwise it is
completely useless.

~~~
sjs
I must not understand monads. I thought I knew the basics of how to use them,
and have used them in the little bit of Haskell I have written, but that makes
no sense at all to me.

What are the apples and oranges supposed to represent? Why would anyone want
to put oranges in the spacesuit? When "you just take what you need", who is
the "you", and are they swimming around in the ocean? Is the ocean significant
at all?

I might be wasting my time trying to understand this analogy. I'm not up to
speed on terms Haskellers throw around such as fields, rings, category theory,
etc. Haskellers are welcoming of newbies and eager to teach, but are on a
vastly different level than the average developer causing some of us to feel
we're not smart enough to wield Haskell. Unfortunately even some basic
concepts are so abstract that trying to explain them in concrete terms often
leads to gibberish such as dons' analogy there.

I realize dons probably wrote that for the monad-savvy crowd as kind of an
inside joke, perhaps I'd do well to just move on.

