Skip to content

Instantly share code, notes, and snippets.

@enolan
Created November 13, 2015 03:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save enolan/8bdc5c80a7628dad048c to your computer and use it in GitHub Desktop.
Save enolan/8bdc5c80a7628dad048c to your computer and use it in GitHub Desktop.
Haskell: The Bad Parts
Complaining about a tiny program, armchair language design
What's the program? Pia-forward
A utility for setting up port forwarding on the Private Internet Access VPN service
- 103 lines
- Mostly IO
- Deals with exceptions
- JSON
- web service API
Let's take a closer look at what it does:
https://www.privateinternetaccess.com/forum/discussion/180/port-forwarding-without-the-application-advanced-users
Let's look at the code
- cabal file
- 15 dependencies of which 7 aren't bundled and 1 is unneccesary
- not very batteries included arguably 4 of the nonbundled ones should be
- 6 language extensions
- this sort of thing is extremely common, so everyone is using slightly
different versions of the language - the standardization process is very
slow and conservative.
- Main.hs
- 25 lines of imports - I'm sure this is really *bad* but it's not what you
were looking for when you opened the file
- line 32: newManager - it's responsible for pipelining and proxying and
general settings related to HTTP connections.
- You're strongly encouraged to only ever use one, but you have the
opportunity to mess up each time you call something that uses a manager.
It's just one more thing to keep track of. In a language with extensible
effects like Idris, you'd create a Manager effect and every function that
made HTTP requests would require that effect. You'd never name the
manager as a variable, and accidentally using two different managers
would be much harder.
Extensible effects are possible in Haskell, but the type hackery is much
more complicated, at least at first glance.
- line 34: Exceptions! try's type is try :: Exception e => IO a -> IO (Either e a)
it catches exception of a particular type, which is determined by inference
or annotation. It's a little weird, but whatever.
- What's worse is the constant tension between exceptions, which are
unchecked and explicit failure notification. Exceptions bubble, which is
often convienient while Maybe/Either can but don't automatically. If you
want Maybe/Either to bubble you need to use monads, and if you're already
in a monad you need to use transformers which can get confusing fast.
Another case where you'd like extensible effects.
- line 43: named field puns. This extension is easy for me, but it's fairly
common to be working in someone else's codebase and get confused because
they're using an extension you're unfamiliar with.
- line 44: exceptions again. A bit weird. ScopedTypeVariables in play here.
it's also used above. Show type of catches.
- line 54: OverloadedStrings. Way too many ways to use strings. String,
ByteString, Lazy ByteString, Text, Lazy Text.
- line 64: Can't actually complain about this, I just did it in an
overcomplicated way.
- line 67-68: Too many strings again.
- line 69: most nitpicky part. Sleep is threadDelay because of reasons you
don't usually care about when you use it.
- rest is pretty much fine. Can complain about there being too many strings
some more if we really want.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment