Fighting spam with Haskell at Meta (2015)
engineering.fb.com
engineering.fb.com
I had previous Haskell experience, but what struck me was how irrelevant that was.
To their immense credit, they had managed to use Haskell to write an entirely entreprise focused, "boring" business rule backend serving millions of QPS, and people who were not Haskell fans could easily use its sensible DSL to accomplish their goals.
Way too many Haskell projects are about Haskell itself, so this was really fascinating to see.
But all I can think of when I see custom operators professionally is how much of a nightmare it would be the second one has to onboard a junior developer onto the project.
That, or YAML, then a tool to template and generate the YAML, and then a stack of tools wrapped around that, ad infinitum.
A little learning is the cost of not having to write the thing yourself. On the other hand, hell is the non-composable tower of babel that is computing. Total employment by exponentially compounding incidental complexity.
The amount of arbitrary glue, configuration and "cool things" I need to learn-and-forget for every new project is an order of magnitude less than any other language I've used in anger.
Pick up docker or kubernetes and you'll have to learn plenty of stuff. Pick up ory/hydra to do auth in go, same thing.
Check out the age (from FiloSottile) or the rage (Rust port) codebase and they require similar level of understanding.
Generally, any language with a type system advanced enough.
Personally, I think Haskell, or something like Haskell, is going to be reasonably popular in the near future. Functional programming and an expressive type system are great for ML-powered synthesis. You provide the type signature, and the machine fills in the function body. Furthermore, with dependent or refinement types, the solution can be verified to be correct.
Here is how to read a text file in Haskell (I assume a standard way):
https://stackoverflow.com/questions/7867723/haskell-file-rea...
From a Python tutorial:
https://www.pythontutorial.net/python-basics/python-read-tex...
I could be biased, but it certainly seems like Python has a lot less conceptual hurdles here. You basically specify a filepath and then for loop through each line and print. The Haskell solution requires more steps and theory.
I know Haskell is a very awesome and cool language and super powerful for all kinds of uses that Python may be inferior at (example compilers). You'll get no argument there. I'm just pointing out that I think wide adoption may be difficult. I drank the koolaid and read dozens of blog posts and a couple of Haskell books and really wanted to make it work. I'm an engineer and like math and puzzles and learning new programming languages...and yet I couldn't make it that far (without sinking a whole lot more time into it than I had) and ultimately gave up.
Really? The suggested Haskell solution is
main = do
contents <- readFile "test.txt"
print . map readInt . words $ contents
which is no more complex than the Python code. The entire file contents is read as a String into content. words breaks it into a list of words, and readInt parses each word into an Int. Finally the list of ints is printed.With the python version, you see the file handle as a variable and then for loop on that iterable and you can print each line or parse it however you want. Even when I was learning to program and had no clue what an iterable object was, there seemed to be an obvious three line idiom for processing text files that was easy to use.
Instead of things going boom and spewing a stack sometimes they just go Nothing instead, you don't need much more understanding than that. There is a lot of syntax in Haskell that is somewhat hard to understand at first but you don't actually need it to write stuff yourself.
This idea that you need to talk a particular way and be able to teach CS theory at uni and whatnot to build stuff in Haskell is probably not very healthy.
I'm not saying you need any CS theory to write Haskell or that it's super hard. But I think the learning curve is pretty steep, and it's hard to write code without a good understanding of the concepts. Just tweaking until it type checks isn't going to cut it.
Consider this code. Generated from ChatGPT. This is supposed to be a simple command line option parsing. I don't think it's obvious what all the operators mean.
Sure, you can try to reuse this, and extend it. But I think sooner than later you'll be stuck with some undecipherable error messages and you'll have to sit down and understand what it all means.
data Options = Options
{ optVerbose :: Bool
, optInput :: String
} deriving Show
optionsParser :: Parser Options
optionsParser = Options
<$> switch
( long "verbose"
<> short 'v'
<> help "Enable verbose mode" )
<\*> strOption
( long "input"
<> short 'i'
<> metavar "FILENAME"
<> help "Input file name" ) open System.IO
(File.ReadAllText "text.txt").Split()
|> Seq.map int
|> Seq.iter (printfn "%d")
It doesn't really matter on simple expressions but as you keep chaining the operations the reverse order gets more difficult to follow.The python version simply loads the whole file into memory. The equivalent Haskell would be to call Data.Text.IO.readFile. What would the python version of the lazy parsing / processing of a file look like ?
I tried just pasting the code into the window, but never learned how to format HN on mobile. Sorry for the confusion. After the colon there are indents, so 3 separate lines.
with open('testfile.txt') as f: for line in f: print(line.strip())
(I also always forget how to format comments, the ref is here: https://news.ycombinator.com/formatdoc )
Also, I wouldn't recommend starting FP with Haskell. It's hard, mostly because of the monads (and laziness can make things more confusing too). Also the syntax which is confusing because indentation is meaningful in some places, but not others.
On the other hand, a language like Scheme is really super easy. Even OCaml is super simple if you stick to the classic ML subset of the language which can take you a very long way. These languages have been using to teach programming to complete beginners. Seriously, OCaml is arguably simpler than Python, and without doubt order of magnitude simpler than C++.
If you're familiar with things like lambda, closures and functions like map or filter in Python, you already know most of what you need to write OCaml code.
I don't think most of FP is at all hard for most programmers to understand. Hell I interviewed plenty of Javascript devs that couldn't solve a simple fizzbuzz level question because they didn't know how to do for loops - they only knew how to use `.forEach()`!
The hard bits in Haskell are:
1. Purity. I get it, but it definitely makes things harder.
2. Weird syntax: pervasive currying / single argument functions / lack of grouping syntax / waaaay too many operators, etc. E.g. from your link:
let singlewords = words contents
list = f singlewords
This is just a collection of words. No clue of what is a function call, what is a variable name, etc. Allow me to recreate the experience of reading Haskell, if you think the above is reasonable: file read lines put
lines iter while cbr empty
for do <~> x IO::print
3. General unfriendliness to people who aren't hardcore Haskellers. E.g. suppose I want to match a regex. In Go, Python, Rust, Javascript, etc. there's one obvious way to do it. Haskell? How about you pick between 8 alternatives... https://wiki.haskell.org/index.php?title=Regular_expressionsThere are other flaws, like global type inference & lazy IO, but I think those are the main ones.
let list = map read (words contents)
The equivalent Python is basically this: list = [int(word) for word in contents.split()]
I'm just writing this for the benefit of others here.Haskell doesn't have operators, it's based on expressions consisting of function compositions, some of which can be infix and look like operators.
> This is just a collection of words. No clue of what is a function call, what is a variable name, etc.
That's by design, because all of them are expressions that can either reduce immediately or require runtime data to reduce fully.
> How about you pick between 8 alternatives...
How about you pick either of those and start using for real, and then come to the point it either works well or you find inefficiencies to look specific alternatives for? It doesn't take much.
https://clayshentrup.medium.com/oop-vs-fp-182475457a01
if you want the oop style you can just change "foo bar baz" to "bar `foo` baz" which is effectively the same thing as doing bar.foo(baz), because for some reason people are obsessed with passing an argument by putting a dot after it.
It seems like Haskell is a good match for the requirements in this case, too. Implicit concurrency and enough robustness from the type system that new filters can be deployed directly to production plays to the strengths of the language. It doesn't feel like the usual solution in search of a problem.
I do not hire anything who does not get outside of their comfort zone or who is intimated by syntax they haven't seen. If a programmer rejects Haskell or OCaml by giving me a good critique of say, Hindley Milner style type systems†, that's a good programmer I'm willing to hire even though I don't necessarily agree.
†: I've been asked exactly this question during an interview: to critique HM style type systems. As a candidate I felt this style of questions gave me a far better way to display my knowledge and experience than Leetcode style questions.
Today's Freudian slip award goes to...
The way your comment reads, it's like you're saying you'll never hire someone who rejects speaking to you in Esperanto unless they offer a good critique of its grammar.
It’s great at being a declarative configuration language though…. Even if it’s ugly.
Hell even if it were a perfectly designed language (it isn't) I still would be put off by it because I don't really want to be a professional full-time packaging engineer.
I think Bazel got a lot right by using a subset of Python. Basically nothing to learn language-wise.
Oh you mean the things that look like operator, behave like operators, and are commonly referred to as operators by Haskell programmers - even in the Haskell Wiki? Those operators?
https://tech.fpcomplete.com/haskell/tutorial/operators/
https://github.com/haskellcats/haskell-operators
https://wiki.haskell.org/index.php?title=Infix_operator
What exactly do you think an operator is?
You're a prime example of a person who reads a page on the Internet and takes it at a face value without understanding the meaning and the context behind the words:
> One of the most common operators, and source of initial confusion, is the $ operator. All this does is apply a function.
Did you miss the part that says "All this does is apply a function"?
Here's how you define (+) among other things: https://hackage.haskell.org/package/ghc-internal-9.1201.0/do...
instance Num Int where
I# x + I# y = I# (x +# y)
whereas (+#) is defined as a bridge from unboxed ints provided by the runtime to their efficient low-level machine representation, accomodating all instances of a data type defined as `Int = I# Int#` (the left-hand side is the name of the type, the right-hand side is the name of a constructor that accepts a single value of type Int# - the low-level primitive provided by the GHC implementation).The (+) in `I# x + I# y =` is a function definition in infix form. That's the same form you can use for any other infix function if you wish so:
ghci> a `iAmHelping` b = a b + b
ghci> :t iAmHelping
iAmHelping :: Num a => (a -> a) -> a -> a
> Oh you mean the things that look like operator, behave like operatorsLet me see your regular operators do the following, then we talk:
plus = (+)
a = (2 +)
c = (2 `plus`)
c' = plus 2
d = (+) 2
d' = (+ 2)
e = 2 `plus` 2
f = 2 + 2
I'm glad that's the only thing you object to, though.Did you miss the part where it's called an "operator"? Your claim was that Haskell doesn't have operators.
I guess you're a prime example of someone that can't remember what the debate is.
> I guess you're a prime example of someone that can't remember what the debate is.
There's no debate, you asked me why I said that you weren't solid. And I show you that you don't have a solid foundation in the tool that you opine on. You refuse the ground truth presented to you in code, and you opt to someone's verbal description of an infix function being an operator. You use a second-hand opinion to form your mental model about a thing, and then you opine on the thing with that invalid foundation that was made for you (by some believing individuals claiming it's the best approach), so that you don't feel intimidated from the get-go. No wonder you don't know why there's no distinctive difference between functions and reduced values in syntax. And that's not only Haskell, you also don't know Nix enough to understand how silly your take is about a subset of Python being better than a specialized language for the problem of cascading graph changes in build systems.
Search for Haskell.
Good to know you didn't have any real reasons to think I'm not a solid programmer anyway. Bye.
> Good to know you didn't have any real reasons to think I'm not a solid programmer anyway.
I'd easily demonstrate more reasons why you're not solid, I will probably do it in the future if I see more of your silly opinions on the things you don't know. But thankfully, in the context of Haskell the comments have already shown your true lack of any substantial knowledge. I hope you will at least obstain from commenting on Haskell and Nix in the future, don't embarrass yourself.
For starters, you compared Nix the ecosystem to Starlark, perhaps the smallest aspect of Bazel. But Bazel (the ecosystem) has a horrendous documentation problem as well.
I grant that "Nix" is a very overloaded term, but it seems like you don't know which part of Nix you are even referring to. "Somewhere... in all of Nix" is not something that makes any sense.
I fully admit that Nix has a steep learning curve. Very steep. But I don't think you know enough to give a thoughtful critique.
No I compared the Nix language to Starlark.
> but it seems like you don't know which part of Nix you are even referring to.
Incorrect. I was referring to the Nix language, as was hamandcheese. He literally said it explicitly. I don't know how you could miss that.
> I fully admit that Nix has a steep learning curve. Very steep. But I don't think you know enough to give a thoughtful critique.
You're proving my point here. The very steep learning curve and "you need to be an expert before you can use it" aspects of Nix (both the language and the package manager) are exactly the problem.
Let me put it another way. Imagine I gave you a vacuum cleaner, and you tried to turn it on but it had a very complicated startup sequence so you needed to read the manual... but the manual was written in Egyptian hieroglyphics. You take it back to the shop and they say "nah this is a perfectly good vacuum cleaner. Sure it has a steep learning curve but you just need to spend some time learning it before you can criticise it".
That's obviously stupid right? For most people package management is like vacuum cleaning. It's a necessary task they want to think about as little as possible.
Nix is written by a vacuum cleaner collection & restoration club, who assume that everyone in the world is as obsessively into vacuum cleaners as them.
I am hamandcheese. I replied to your comment. You didn't describe a single aspect of Nix-the-language. You wrote this:
> What does setting `foo: true` do? You can't go-to-definition on it. You can only hope it is well documented (it isn't) or try to find the place that reads that key ... somewhere... in all of Nix... Good luck.
It's appears (despite the wrong syntax, another clue that you are not particularly well informed) that you are referring to a NixOS module option. Or maybe a Home Manager module option. Either way, not a language feature.
Ah yes :D
> Either way, not a language feature.
The language feature I am talking about is that it is completely declarative. You can argue semantics if you want but Nix chose to use a completely declarative language for configuration and that was IMO a bad decision.
Bleacher Report is a funny example: it used to be a darling example of Elixir, where a migration from Ruby -> Elixir claimed a move from "150 Ruby servers to 5 (probably overprovisioned) Elixir servers."[2] But then management and politics got scared, moved it all to more conventional tech, and the whole system suffered (see this legendary post[3]).
Fred Hebert describes a similar thing happening with a migration from Erlang deployments to Go/Docker/immutable, where you lose some pretty valuable capabilities by migrating to more conventional tech.[4]
I don't see this changing anytime soon -- we came of age when it was viable to attract investment with the promise of tech innovation. These days, those are liabilities because managers misunderstood the "Use Boring Technology" post the way consultants bartardized "Agile" (taking decent advice and misunderstanding it into something wholly different and horrifying). The result is you've got companies with customers in the 1000s using k8s, calling it "simple" and "Boring," whereas that same company would be called amateur if they did things like stateful deploys on-prem.[5]
At least we'll always have WhatsApp.
[1]: https://www.baldurbjarnason.com/2024/react-electron-llms-lab... [2]: https://web.archive.org/web/20170204160005/http://www.techwo... [3]: https://www.reddit.com/r/erlang/comments/18f3kl3/comment/kct... [4]: https://ferd.ca/a-pipeline-made-of-airbags.html [5]: https://morepablo.com/2023/05/where-have-all-the-hackers-gon...
I've coded in so many languages in my life, but the job market pull from Ruby always drags me back, with time, I began to really love and appreciate what Ruby is.
I still find Ruby a bit niche than other mainstream languages like Java and Python, I bet that if I had >5 years of Java, the Java market pull would be higher than Ruby and I'd be doing Java.
Unfortunately, I have never seen hotswapping of Haskell code anywhere else. All I can find is this library [0] by Simon Marlow.
Together with Cloud Haskell [1], hotwapping would allow to create pretty flexible distributed systems à la Erlang/OTP.
[0]: https://hackage.haskell.org/package/ghc-hotswap
[1]: https://github.com/haskell-distributed/distributed-process
Who?
Haskellers are proud of a few projects that use Haskell (pandoc, xmonad, etc.) and they boast about how they can achieve the same thing as i3 but in much fewer LoC. But frankly I don't think #LoC is a good metric for gauging a language's value.
That would not be news, Haskell is famous for being an academic language without many real world applications. C++ is famous for being practical.