We came up with a way of reifying specifications of web APIs at the type level, and built a web framework around it. The basic idea was, I believe, first developed by Ralf Lämmel and Klaus Ostermann [0], as a Haskell solution to the expression problem, but I think it's the first large-scale application of these ideas.
Checking for correctness and deriving functionality from that comes pretty naturally after that, so it's easy to reduce boilerplate (e.g., generate client libraries and documentation automatically) and make static guarantees (e.g., that a link exists within an API, and won't 404).
Having the type-system statically verify that your API is correct and that your usage of it is correct, but oof, that syntax is quite hard to read. I wonder if you could use Template Haskell to generate that from an easier-to-read routes file similar to Yesod.
We had QQ originally, but removed it because it wasn't extensible (you can define new expressions in the type-level DSL, but you couldn't add them to the QQ). We've figured out how to do that now, and there's an issue someone opened asking for it back (https://github.com/haskell-servant/servant/issues/55), so it'll probably soon return.
You do get kind of used to it, though, for what it's worth. And you can always break things up. E.g.:
type MyAPI = "users" :> Get '[JSON] User
:<|> "users" :> ReqBody '[JSON] User :> Post '[] ()
Can generally be rewritten as:
type GetUser = Get '[JSON] User
type PostUer = ReqBody '[JSON] User :> Post '[] ()
type MyAPI = "user" :> (GetUser :<|> PostUser)
Checking for correctness and deriving functionality from that comes pretty naturally after that, so it's easy to reduce boilerplate (e.g., generate client libraries and documentation automatically) and make static guarantees (e.g., that a link exists within an API, and won't 404).
[0] http://www.informatik.uni-marburg.de/~kos/papers/gpce06.pdf