Matt Parsons writes about production Haskell, type-level programming, type safety, and software design practices.
HN commenters recommend it as a top Haskell resource and link its posts on application structure, type classes versus interfaces, and GADTs.
> The front end is beginning to 'get it' with the likes of Typescript, Flow, Elm, Reason, Purescript, etc, and I can only hope that their good work and excitement will eventually flow back to us on the back-end...
I really have to say it’s been a joy writing the backend for a Bitcoin-related app[1] in Haskell. It took me a while to adapt to Haskell — probably a couple of years or so — but in the end it has left me with such strong compiler guarantees that I can literally grep through my codebase for “error|throw” and see where a 500-error may be thrown by the server.
The only somewhat laborious part is categorizing all the exceptions, that can be thrown when performing requests, into retryable and non-retryable ones, so requests that throw these intermittent, retryable errors are automatically retried (the retry package makes automatic retrying super easy).
This post[2] gets to the crux of how my coding has adapted over the years: getting rid of runtime-decisions (e.g. looking at a Maybe value at runtime to figure out what to), by moving the relevant parameters to the type level, such that program flow is determined at compile-time by which type an operation is parametized over. For example, the app is parametized over two types: DB implementation and Blockchain implementation[3], which makes the compiler construct a program for these specific implementations at compile-time, rather than making it a runtime-decision based on reading a config file at startup.
[1]…
> Thank you, I've read the links, but without an example of Haskell/Scala code the benefits for any particular real problem are not clear to me.
Do you have cases where you've used AOP/reflection/metaclasses/decorators/macros or similar "magic"? (Exceptions or mutable variables sort of work as examples, but most people don't have experience of working on codebases that don't have them). It's hard to give simple examples because these things exist to solve complex problems - one should never reach for a monad where a plain function will do.
> Specifically, speaking of the second link, what happens if you need to get rid of two types of 'hell' at the same time?
There are several possibilities. First, it's worth noticing that most forms of "hell" are fine on their own, it's the interaction between two that tends to be surprising - e.g. https://glyph.twistedmatrix.com/2014/02/unyielding.html talks about how important it is to explicitly manage async, but it's making an underlying assumption that your language will have mutable variables. Unmanaged, pervasive mutation in a language without async is ok; equally, unmanaged, pervasive async is ok in a language without mutation. So often you can get away with managing one effect and letting the other happen naturally according to the program control flow. (This is less true in Haskell because laziness is built into the language, so Haskell has kind of "spent its freebie" there - but it can be a very effective style in…
I'm going to go out on a limb and suggest this stack (especially if you're into functional programming):
Haskell + Elm or PureScript + Postgres
The best part about it is that it helps you catch many errors at compile time, instead of having your app crash at run time.
For keeping your Haskell and Elm data types in sync while maintaining sanity, I recommend elm-export [0], which will automatically generate the Elm types and JSON decoders from your Haskell code, using GHC Generics. Watch the author talk about it [1].
Choosing Elm over JS already gives you a head start, but you might hit a productivity wall at some point, and PureScript gives you all that extra power with its advanced type system. Some related discussion here [2].
Check out Matt Parsons' blog [3] for more on Haskell, Elm and PureScript.
Lastly, for database you can use Persistent [4] which gives you a type-safe API for data modelling and data store, and can automatically generate and perform migrations for you.
[0]: https://github.com/krisajenkins/elm-export
[1]: https://www.youtube.com/watch?v=sh4H8yzXnvw
[2]: https://www.reddit.com/r/haskell/comments/569cax/
[3]: http://www.parsonsmatt.org/
[4]: https://hackage.haskell.org/package/persistent
P.S. Do take my suggestions with a grain of salt as I don't have much experience with any of the fancy JS frameworks.
> "simplest way to do BasicAuth"
The simplest way to do BasicAuth in Haskell isn't Servant. I feel like you're intentionally misinterpreting what I'm saying.
As for resources, here's a great intro to type-level Haskell:
http://www.parsonsmatt.org/2017/04/26/basic_type_level_programming_in_haskell.html
> For some reason, functions that operate on types are called type families.
That's the kind of thing you need to know.
I doubt the average JS user knows how V8 optimizes execution of code, or whatever. That's what your insistent complaints about type theory amounts to: none of the articles I'm linking to mention inference rules or type judgments or what-have-you. That's what all the research papers about Haskell are for, which you do not need to read to use this language. Even advanced Haskellers don't do type theory (category theory, maybe, not type theory). Only a couple of people working on the compiler do.
More DataKinds:
https://stackoverflow.com/questions/20558648/what-is-the-datakinds-extension-of-haskell
http://ponies.io/posts/2014-07-30-typelits.html
https://www.schoolofhaskell.com/user/k_bx/playing-with-datakinds
I think there's perhaps an analogy to be made between FP terminology that comes from math, and is therefore unfamiliar to non-math people, and type theory notation such as this. For example, an FP "functor" comes from the idea of (endo)functor from category theory, but people try to rename it (e.g. "Mappable") to make programmers understand it better. In the same way, you're proposing to rename the turnstile to an arrow symbol.
The article "Why 'Functor' Doesn't Matter" [0] is relevant.
In this case, the turnstile symbol comes from logic, and means "entails": Given the information on the left, you can derive the judgement on the right. There is an important distinction to be made between the turnstile and the double arrow [1].
For what it's worth, I haven't formally learned higher-level math, but I've been able to pick up type theory notation just by getting used to it. I can't necessarily explain how; it was just a gradual thing for me.
[0] https://www.parsonsmatt.org/2019/08/30/why_functor_doesnt_matter.html
[1] https://math.stackexchange.com/questions/286077/implies-rightarrow-vs-entails-models-vs-provable-vdash
> https://www.parsonsmatt.org/2018/11/03/trouble_with_typed_errors.html
Nice. I've asked for a way to do that in the past and never found a good answer, in any language! It's not exactly conventional Haskell though, is it? What I really want is first-class support in the language - something like checked and unchecked exceptions in Java, except that if a method declaration lacks a `throws` keyword then all the checked exceptions are inferred by the compiler. For example, the compiler might add `throws A, B, C` to a method that lacks a `throws` keyword. Now if you want to assert that a certain method throws a certain exception, you could write `throws A, *` which means "If this method does not throw an exception of type A, I want a compiler error. If this method throws additional exception types, infer them as usual." Omitting the asterisk (eg `throws A`) would disable the inference and thus would work like a normal `throws` in real Java. You should also be able to assert that a certain exception type is not thrown, for example `throws * except F, G` or something like that.