Servant allows you to write a type that describes a server/API, and automatically derives a specification for it. You can then write handlers for the different API endpoints, and it will only compile if every endpoint is correctly handled. That includes content types, status codes, and so on, everything is verified, and things are often derived for you. All (de)serialization to/from JSON is automatic (although you can write your own instances if you want your JSON to look a certain way).
For example, from the Servant docs:
type API = "position" :> Capture "x" Int :> Capture "y" Int :> Get '[JSON] Position
:<|> "hello" :> QueryParam "name" String :> Get '[JSON] HelloMessage
:<|> "marketing" :> ReqBody '[JSON] ClientInfo :> Post '[JSON] Email
GET /position/:x/:y/ returns a Position object as JSON
GET /hello?name=whatever returns a HelloMessage object as JSON
POST /marketing given a JSON request that represents a ClientInfo object,
returns an Email object as JSON
You can then write functions of the following types
handlePosition :: Int -> Int -> Handler Position
handleHello :: Maybe String -> Handler HelloMessage
handleMarketing :: ClientInfo -> Handler Email
If you add a new API endpoint, and forget to write a Handler for it, you'll get to know.
If you change the Position type to work with x, y, and z coordinates but forget to update your handlers, you'll get to know.
If you'd like to also allow clients to request HTML instead for some endpoint, just change the '[JSON] to '[JSON,HTML]. The Haskell typesystem will make sure that someone requesting a text/html Content-Type doesn't get hit with a 500 or something.
Note the Maybe String there: if you wrote a handler for the /hello endpoint that had the type String -> Handler HelloMessage, Servant would complain: you're expecting a query parameter, which the client doesn't have to provide. This is in stark contrast to, say, the "NoneType has no attribute whatever" problems that one risks having to face with Django: if it compiles, it meets the spec.
Of course, static typing won't prevent you from responding to /hello with Lovecraft quotes.
> I start getting lost in layers of monad transformers
Handler is actually a monad transformer, and it's a great first one: it's essentially
type Handler a = ExceptT ServantErr (ReaderT Config IO) a
* throw a ServantErr
* read values from a Config object
* perform IO actions (read files, make other network requests, log things, "fire missiles")
After a couple of months, you realise it's not too wrong to say you understand how to use them, and you slowly begin to be able to rely on the type system for support. Libraries like Servant are a great example of how this can really, really help. I stopped to think last week how similar it is to pair-programming with a really intelligent but sometimes obtuse friend who likes pointing out how "this doesn't follow from your assumptions", all the way from
> "Wait, but you can't add two books together."
> "What if someone PUTs to /login?"
which is what we had above, to
> "And what happens if I try to withdraw all my money exactly when the payment I've made to you is getting executed?"
(in this case, you discover the wonders of software transactional memory). A better typesystem allows you to let the compiler handle more of the busywork that you'd originally have written tests or comments for.
It is sometimes confusing starting out (when one discovers that 3 isn't an Int, but a "Num a => a", for instance), but it gets better. Once you get past the Project Euler/"look ma, infinite lists!" stuff, "real" Haskell does have a tendency to make people underestimate how much they really know, but as I've discovered, taking the plunge reveals that one has progressed much farther than one thinks.
Also, #haskell on Freenode is one of the friendliest places I've encountered on the internet.
: Here's a hilarious example (NSFW language warning): https://gist.github.com/quchen/5280339
Corrode is a C-to-Rust converter written in literate Haskell (as in, it's like a blog post that you can compile and run, to give a slightly strained analogy).
webshit weekly actually "quoted" a previous HN comment of mine about it, in which I was falsely slandered: I'm not a Rust evangelist!
I wrote a small "script" to make tmux status bars a while back that was a good example of how fun "scripting"-style thing can be in Haskell.
And there's always XMonad!
> This is equivalent to an API that defines these three endpoints:
It would be cool to use the text that follows this as the spec; i.e. write a converter from this description to Haskell.
> GET /position/:x/:y/ returns a Position object as JSON
Or, you know, a quasiquoter (which is sort of like a Haskell macro thing, although Template Haskell is closer to that).