
A Dead-Simple Web Stack in Haskell - phonebucket
https://williamyaoh.com/posts/2019-11-16-a-dead-simple-web-stack.html
======
cousin_it
I think a simple web stack should aspire to the simplicity of the PHP stack,
twenty years ago:

1) Create a directory "foo" on your server

2) Make a file called "bar.php" in that directory

3) Write "Hello World!" in the file

4) Open [http://localhost/foo/bar.php](http://localhost/foo/bar.php) in the
browser and see "Hello World!"

Lines of code or configuration so far: _zero_.

5) Add some actual code to bar.php

6) Reload the page and see the result

Number of steps to rebuild or restart: _zero_.

Then you can add templating, routing, logging, testing, anything. I'm not
married to the PHP language, it's a mess. But the idea of keeping simple
things simple is very important, and from what I see of today's developers,
that idea has been _utterly forgotten_. Memory-holed. It's astonishing, I can
barely believe that it happened.

EDIT: Sorry, I just said this offhand and didn't mean to hijack the
discussion. Feel free to downvote, so something more on-topic gets on top.

~~~
thewarrior
1) Install node js 2) npm install express 4) Define handler

Seems simple enough ?

~~~
girvo
I disagree with the others, and think this is indeed simple enough without
caveats about setting it up (setting up WAMP is honestly just as much effort —
not much), and what I’ve used to teach friends who wanted to learn some web
programming with zero initial understanding.

Explaining routes and handlers was a bit hard, compared to files. I wonder if
there’s a “file based” version for Node that’d work like and express handler
for the files route?

------
bad_user
I love this.

I'm a Haskell newbie, I only do Haskell in my spare time, only sometimes, so I
don't have time to hunt around for recipes. Kudos to the author, I'll surely
borrow from this.

\---

I also built a simple web server in Haskell for triggering commands via
GitHub's Webhooks [1].

So if you're interested in a very simple, yet useful web app to analyze, see:

[https://github.com/alexandru/github-webhook-
listener](https://github.com/alexandru/github-webhook-listener)

My use-case is to serve a static website using my own VPS, but have Travis
build the website (via a static generator) for me. After Travis builds the
website via Jekyll, it pushes it to the gh-pages branch, then GitHub pings my
server via the installed webhook, which triggers a refresh with the latest
build.

[1]
[https://developer.github.com/webhooks/](https://developer.github.com/webhooks/)

~~~
ghostwriter
Migrate your static site to Hakyll
[https://jaspervdj.be/hakyll/](https://jaspervdj.be/hakyll/) from Jekyll, and
use git subtree ([http://brittanderson.github.io/posts/2018-01-17-hakyll-
and-g...](http://brittanderson.github.io/posts/2018-01-17-hakyll-and-github-
with-subtrees.html)) to publish on GitHub Pages without Travis :)

------
defanor
Not sure what's meant by "simple" here, but it looks quite complicated to me.
Even though I use something similar at work (with Servant), but much simpler
(FSVO "simple") approaches as a hobby:

\- Those frameworks relying on built-in servers, including all their
dependencies, are huge. If a program is linked statically (the default for
GHC/cabal), that's also a very large codebase that doesn't get updated on
system updates. Plain CGI (or FastCGI, if needed) is much more lightweight,
and an external web server usually provides generic logging.

\- postgresql-simple provides more than just protection against SQL injection;
that is implemented in the C libpq, and usable with postgresql-libpq bindings
(which are used by postgresql-simple) too.

\- Database connection information, in case of PostgreSQL, doesn't require a
fancy configuration: libpq reads it from environment variables. In many cases
even those don't have to be set: by default it would try to connect to a local
database, under current user, and to the database matching user name.

\- PostgreSQL itself can compose XML (XHTML/HTML 5), which then can also be
processed with XSLT, cutting out the blaze-html dependency.

\- Custom logging facilities tend to be a pain to work with, and not necessary
either; there are syslog and journald.

Using all that (and even more) instead of more lightweight/low-level/low-
dependency alternatives seems like a sensible compromise in some situations,
and I don't mean to say that it's somehow bad, but not seeing how it's "dead-
simple". Maybe it's one of those "simple versus easy" cases, or just about
different kinds of simplicity.

~~~
ar_lan
This reminds me of this commit in Redux:
[https://github.com/reduxjs/redux/commit/9276ff0af6400633d673...](https://github.com/reduxjs/redux/commit/9276ff0af6400633d6731b15fed6e70c3561887e).

"Simple" is always a relative term. I agree, if "simple" looks like the
complication in the post, then I'm turned off from the language because I
don't really want to see what "complex" can become.

I have no doubt if I had context of Haskell, I would be able to read that with
no trouble - even without Haskell context I think I can grok it pretty well.
But imagine a newbie to the industry as a whole? It stops being simple at
various stages, very quickly.

------
TazeTSchnitzel
This is exactly the stack I used when I started building a webapp in Haskell,
it was nice. I never finished my webapp though (but that wasn't Haskell's
fault).

------
verttii
This is a great tutorial for getting up and running fast. I started off with
Spock too but grew out of it pretty quickly.

It's painful in Haskell you're learning it but feel you're not able to
generate anything real for the first month(s) with it. Unfortunately, there's
not many up-to-date tutorials like these available for beginners.

~~~
dwcnnnghm
Do you have any recommendations for next steps beyond Spock?

I learned Haskell at uni and am interested in going back to it for web work
(instead of Erlang). Any insights you might be able to share would be really
helpful!

~~~
verttii
Servant. It takes a while to pick it up but it's well worth the effort. You
end up with an automatically documented type safe API and you can even
generate client functions for other languages using Swagger based on the spec.
It has excellent documentation too, the only hard part really is figuring out
the handler pattern.

As for the SQL story, that's more fragmented. I'm still trying to figure out
the best next step myself. I started off with Persistent & Esqueleto but now
looking at BEAM, Selda and Squeal for more control over the queries.

If you use Persistent as the persistence layer, check out Matt Parsons' blog
for a nice intro: [https://www.parsonsmatt.org/2016/07/08/servant-
persistent_up...](https://www.parsonsmatt.org/2016/07/08/servant-
persistent_updated.html)

I'm actually coming from Elixir myself. I have to admit Haskell feels funny at
times but admittedly a lot more robust.

------
smaudet2
My take on the whole "complex versus non complex" debate raging below.

If there were _nothing_ to the exorbitant complexity, there would be no
complaints about modern stacks. It would be sort of, invisible? Like an OS is
mostly invisible these days - that problem space is mature to the point that
its, for most people, an afterthought.

So modern web dev does suck, yes. I am 100% of this opinion.

But the simple solution was re-invented into the 'mess' we have today. It
doesn't scale (well), and although yes it wasn't complex it was a hell of a
lot more complex to add features to, at least ones which weren't simple "hey
change the look and feel of some widget" or "hey do some flashy UI thing".

I think that's probably a generally true statement, when a lot of work is
needed to develop something (physical or otherwise), it will suck, because it
still needs to be worked on? So anything being used by the industry and
"modern" will have pain points, period.

Once its actually good, it tends to be simplified to the point that it becomes
invisible. OFC, you can still use WAMP or whatever, or weebly, but nobody
cares about WAMP or weebly anymore, outside of whatever small pet project or
blog you run.

------
_hardwaregeek
Let's walk through my thoughts.

Hmm, okay so Spock looks nice. Neat! The routing code looks very easy to read:

    
    
      main = do
        spockCfg <- defaultSpockCfg () PCNoDatabase ()
        runSpock 3000 $ spock spockCfg $ do
          get root $ do
            Spock.html "<div>Hello world!</div>"
          get "users" $ do
            Spock.json (A.object [ "users" .= users ])
          get ("users" <//> var <//> "friends") $ \userID -> do
            Spock.json (A.object [ "userID" .= (userID :: Int), "friends" .= A.Null ])
    
    

Alright, let's figure out how this works.

    
    
      runSpock :: Port -> IO Middleware -> IO () 
    

Okay that makes sense. Takes in a port, some middleware and spits out IO. Not
crazy. Hmm, okay these dollar signs are making things a little confusing, but
I can figure out that spock returns an IO Middleware and therefore we can see
that spock takes the output of the right do block along with spockCfg. Not the
easiest to scan at first glance because of the right to left reading but okay.

Let's look at get.

    
    
      get :: HasRep xs => RouteSpec xs ps ctx conn sess st 
    

Ah, okay, so uh...this doesn't appear to be a function. Oh, I see, here it is:

    
    
      type RouteSpec xs ps ctx conn sess st = Path xs ps -> HVectElim xs (SpockActionCtx ctx conn sess st ()) -> SpockCtxM ctx conn sess st ()
    

Well I'm not really sure what xs, ps or st are. And HVectElim doesn't tell me
anything. Path is pretty clear and SpockCtxM is a monad of sorts. But what the
hell is HVectElim? Alright whatever, let's just figure it out via the
signatures of Spock.html/Spock.json. Which are...nowhere to be found in the
documentation. I looked them up in the index and nope, no entries. I'm very
confused about what HVectElim is and why that would be a fine name.

I can continue past this point, but you get my gist.

This is actually way better than my other experiences with Haskell and web
dev, but it's by no means an easy or fun experience reading this code.
Variable names like xs, ps, etc. are great when you're dealing with a generic
list but they get old when that's the only documentation (because "type
signatures are documentation" is _totally_ infallible). And a name like
HVectElim is just baffling. What, did they charge you per letter? Did half
your keys fall out? Why is that a good name?

I really want to use Haskell for something useful. But I'm running into pain
points trying to understand a glorified Hello World. Now granted I'm by no
means a great Haskell programmer. But that's kind of the point. I understand
monads. I get functors and applicatives. I should be able to write a dumb
Hello World app dammit.

~~~
defanor
That documentation indeed could be improved, but I guess the intention here is
that basic usage is learned from the tutorials. Though it's possible to figure
from that documentation alone as well (I'm reading it for the first time too,
but using Haskell often): Path construction collects types of arguments into
xs, then HVectElim (heterogeneous vector eliminator, or maybe "elimination")
unwraps it into an appropriate function type. xs are those types, ps is
PathState, st usually stands for "state".

A dumb web "Hello, world!" can be written with just putStr though, using CGI
(along the lines of what I've mentioned in another comment here). Or perhaps
with Network.CGI ("cgi" package), which is quite a bit more straightforward
than that RouteSpec, especially if one wants to quickly understand how it
works.

I had a similar experience (wanting to use a language for something useful,
but mostly failing to) with Rust, and maybe Python (just s/wanting/having/ in
that case), but I guess the problem with that was primarily in trying to find
out what's the "right"/idiomatic way to do something while there's none, or to
apply practices from other languages. And maybe in not having a suitable
project idea at the time. Most likely there can be more reasons of that, but
they seem to go away with practice, clear goals, and/or motivation.

~~~
_hardwaregeek
Basic is the key issue here. Sure I can type this out and get a simple program
working (although if I make a transcription error, I'm not super sure I can
reason through the code). But if I want to build anything beyond the basic, I
can't rely on the three whole tutorials on Spock^[1]. I need to start
understanding the library.

But at that point reading the docs is pretty frustrating. Even with your
explanations, I can't see how HVectElim or xs, ps, st are okay names. Sure,
it's idiomatic. But that doesn't mean its good. What was wrong with types,
pathStates and state? Sure it's less terse, but in this case that's fine. Or
if they wanted to keep the same naming, at least explain it in the docs!

Having built a web app in Rust, I've certainly run into pain points, but
nothing at this level. A library like Rocket has so much more documentation
and careful examples^[2]. But also, the interfaces in Rocket are fairly
simple. You write a function that returns a type that can be converted into a
response. Anything you can serialize with serde can be converted into a
response. With Haskell, the interfaces are that my route defining functions
return a

    
    
      SpockCtxM ctx conn sess st ()
    

which is actually a

    
    
      SpockCtxT ctx (WebStateM conn sess st) 
    

With WebStateM being

    
    
      WebStateT conn sess st (ResourceT IO) 
    

Yeah...I can kinda infer that these are chained monads but I have no clue what
bind or return does. I assume SpockCtxM is a reader monad and WebStateM is a
state monad? But that should really be described in the documentation.

I see that the other libraries in the tutorial are better documented than
Spock, so hopefully the Haskell ecosystem is getting better at this. But I'm
still not sure I'd want to build a web app in Haskell (how's package
management? Did you guys ever resolve cabal vs stack?)

[1]: [https://www.spock.li/tutorials/](https://www.spock.li/tutorials/)

[2]: [https://api.rocket.rs/v0.4/rocket/](https://api.rocket.rs/v0.4/rocket/)

~~~
defanor
Longer names can be seen as unnecessary and duplicating information. One has
to balance between being verbose and laconic when it comes to naming in
general, but judging by the lack of annotations in this case, not much care
was put into it being easily readable. It may be useful to report it, once
that's an issue: chances are it just seemed obvious to the author, as it also
often happens to code in general, and acceptable to users.

If you see an insufficiently documented and/or straightforward API, and
there's no good reason to use that one in particular, I think it may also be a
good idea to look for alternatives, in any language.

> how's package management?

I think it's fine, but there are different opinions on how it should be. I
prefer a better system integration and shared libraries coming from system
repositories (which is usable with Debian), Cabal works fine, Stack seems to
be popular, some seem to use Nix.

> Did you guys ever resolve cabal vs stack?

"Resolve" as in choosing a one true way to build/install things? I don't think
so, but there is this annual "state of Haskell" survey that includes build
tools, which was closed just a few days ago this year (but no results
available yet, afaik), though there are the results from last year [1]. "State
of the Haskell ecosystem" [2] may be of interest too.

There certainly are imperfections/drawbacks (and/or varying
approaches/opinions/preferences), and it may not be a good choice for you for
making a dynamic website (I think in many cases the overall best choice is the
language one is most fluent in at the time), but my replies were mostly with
the "I really want to use Haskell for something useful" sentence in mind:
being exposed to worse (or at least less suitable) bits and running into a
dead end can be unfortunate in that case.

[1] [https://taylor.fausak.me/2018/11/18/2018-state-of-haskell-
su...](https://taylor.fausak.me/2018/11/18/2018-state-of-haskell-survey-
results/)

[2] [https://github.com/Gabriel439/post-
rfc/blob/master/sotu.md](https://github.com/Gabriel439/post-
rfc/blob/master/sotu.md)

~~~
_hardwaregeek
The reason I ask about package management is because when I do my annual try-
to-use-Haskell-for-real event, I pick either Stack or Cabal, inevitably run
into issues with one and have to switch to the other. Plus I've had situations
where I tried to install packages and the manager gave me the computer
equivalent of a shrug. Contrast that to Cargo which works beautifully and
seamlessly out of the box.

However I hold out hope that Haskell is getting better and easier to use.
Legitimately, this tutorial's code looks a lot better than what I've seen
before. But I'm still dissatisfied with the level of naming and documentation.
I get it that the names may have been obvious to the author, but it's a little
worrying to me that the author didn't think about this fact when writing the
documentation. Plus Spock isn't a young framework. It's my belief that anybody
writing a package should know that interface names are designed to be readable
and understandable. And these names aren't subtly hard to read. HVectElim just
isn't a great name.

That being said, I don't want to just hate on the Haskell ecosystem. I do
really find the language fascinating. I just want some better documentation,
more tutorials and nicer, unified tooling.

~~~
verttii
Stack uses cabal underneath though. What kind of issues are you running into?

Personally I love the language but struggle with all the (necessary) stuff
around it. Configs, deployment, dependency management, editor setup, lint,
auto-formatting... Everything feels unnecessarily painful.

~~~
_hardwaregeek
Generally issues where package management just fails. Also it's just overall
not ergonomic. I end up installing yet another version of GHC a lot (I believe
that GHC version should be handed separately from package versions like
rustup/cargo, nvm/npm, rbenv/bundler). But this info could definitely be out
of date. I might be due for another try at Haskell

~~~
defanor
Not sure if it became much better in the past few years; I seem to run into
issues less and less often (and all were solvable rather quickly), but it may
be simply because of the avoidance of problematic packages and approaches.
Here is a bit more of information though:

\- Cabal 2 supports Nix-style local builds [1].

\- Stack seems to aim very smooth and easy workflow. Though it didn't work
well for me when I tried it (I think I had a version that was too old, and was
rather appalled by the way they suggest to install a new one, with `curl ... |
sh`). And as mentioned above, I prefer the opposite of adding even more layers
on top of cabal and using multiple package managers, in part because it
introduces more ways for things to go wrong. Likewise with Nix outside of
NixOS.

\- I used to find Cabal sandboxes helpful, but not using them these days.
Different versions of the same package can be installed simultaneously, if it
becomes tricky to stick to a single version while using Cabal. And as the last
resort one can wipe out the local package database.

\- Careful versioning could solve some of the package management and
dependency resolution issues, and there is a common package versioning policy
[2] (similar to, but not exactly the same as semver), but not everybody
follows it (in versioning their packages or in pinning dependencies); one
should be careful with packages that don't.

\- Minimizing dependencies can be useful for this, among other things. Without
keeping an eye on it, one can easily find themselves in a dependency graph
with hundreds of packages, often coming pretty much directly from developers
(without additional package maintainers keeping an eye on them following any
standards or being compatible, though it's supposed to be better with Stack),
some of which may misbehave at some point.

\- On some systems it is viable to use a system package manager for Haskell
package management.

But possibly my experiences are not representative either: as mentioned
before, opinions and approaches around it tend to vary.

[1] [https://www.haskell.org/cabal/users-guide/nix-local-build-
ov...](https://www.haskell.org/cabal/users-guide/nix-local-build-
overview.html)

[2] [https://pvp.haskell.org/](https://pvp.haskell.org/)

------
asjo
Really happy to read this - I'm using Spock and postgresql-simple, but found
Lucid for generating HTML instead of blaze.

I never got around to wrapping my head around handling configuration, so I
will definitely take a look at configurator.

I also like the suggestions for what libraries to look at next, sometimes
choosing what libraries to go for feels like a jungle for a newcomer.

------
artfulhippo
Haskell is extremely complex, and that’s the point: to isolate complexity at
the language level, where safe patterns can be researched, developed, and then
scaled out to arbitrary applications.

Please call this “Towards a simple Haskell web stack” or something else less
oxymoronic.

~~~
agentultra
> _Haskell is extremely complex_

I extremely disagree. I think C++ is extremely complex. Haskell is simple by
comparison [0].

Once you understand enough Haskell to write basic programs in it this stack is
roughly similar in scope to a Ruby + Sinatra or Python + Flask web
application.

[0]
[https://www.youtube.com/watch?v=PNRju6_yn3o](https://www.youtube.com/watch?v=PNRju6_yn3o)

~~~
seagreen
It depends on what we mean by Haskell.

If we mean "Haskell the latest specification" that's Haskell 2010. I suppose
we could think of it as pretty simple, though lazy evaluation means that any
implementation of it is going to be complicated.

However I don't know of a single company that uses plain Haskell 2010. They
all use GHC (or more complex tools like GHCJS!) and GHC Haskell is a beast:
[https://downloads.haskell.org/~ghc/latest/docs/html/users_gu...](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/glasgow_exts.html)

~~~
lalaithion
And how many companies use a C++ standard? GCC and Clang both provide a large
number of extensions.

[https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/C-Extensions.ht...](https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/C-Extensions.html#C-Extensions)
[https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/C_002b_002b-Ext...](https://gcc.gnu.org/onlinedocs/gcc-4.8.5/gcc/C_002b_002b-Extensions.html#C_002b_002b-Extensions)

~~~
seagreen
OP: haskell is extremely complex.

Responder: not it's not (eg C++ is more complex)

Me: actually haskell is a complex, beastly language, eg check out the language
extensions

You: but C++ is more complex!

Me: i agree

EDIT: Didn't mean to sound snarky, the discussion has just gotten really
layered and I wanted to explain my position.

------
Schoon
To comment on the choice you have when building your frontend, Reflex/Reflex-
Dom also comes to mind.

