Hacker News new | ask | show | jobs
by adamjc 1301 days ago
I know this is a "toy" paper, but is enough to reason as to why few use Haskell in production (ignoring the tooling issue). It's pretty gnarly even for a basic problem.

> Functional programmers! Remember higher-order functions!

I didn't realise utilising HoF meant rolling your own interpreter ;).

Pure FP is very interesting, but having to write entirely without side-effects would be a real challenge for me. I use js+ramda[0], we effectively write our services as a `pipe`, you just stick in transforming fns whereever you need to, e.g.:

  const handler = pipe(
    fn1,
    fn2,
    fn3
  )(event)
The underlying implementation of `fn1` for example doesn't need to be written in an fp paradigm, it can be written imperatively. You could then slowly-slowly chop out the imperative functions for fp ones if you really wanted, but IMHO some problems are best solved imperatively.

My advice is instead: "Programmers! Remember to use the right tool for the job!"

[0] https://ramdajs.com/docs/

3 comments

You're looking at this the wrong way. The paper isn't showing Haskell is gnarly for a basic problem, it's using a basic problem to demonstrate a particular technique that you can use in Haskell. There are plenty of simpler ways to implement this kind of thing in Haskell, and even some that are both simpler and show off some of the more unique features of Haskell. The goal here wasn't "let's find the best way to write fizzbuzz in Haskell", it was "let's show off how to build an interpreter to solve problems, using a small problem everyone is familiar with".

Haskell isn't ill-suited for something like fizzbuzz at all, it's just _also_ suited for things like quickly writing interpreters and solving problems that way when it makes sense.

I find a lot of people don't get pedagogy. They want immediate, authoritative statements on what is good or bad. Haskell isn't really about that. It's about the 10s of equivalent ways to solve every simple problem. That's what makes it fun. And fun is Haskell's secret sauce.
Case in point: it can prompt people to gush about a fizzbuzz implementation.
"10s of equivalent ways to solve every simple problem."

Why doesn't C#, Java, etc have the same problem with perception because they have the same "many ways" to solve problems.

"fun"

brainfuck is fun too and no one uses it.

Because C#, Java, etc are more familiar to most people. People tend to struggle with unfamiliar things.

Haskell is simple even if unfamiliar to you. Brainfuck is complex even if familiar to you.

> Haskell is simple even if unfamiliar to you. Brainfuck is complex even if familiar to you.

I would actually swap these two descriptions. Haskell is complex, because it has many "complications" (in the sense of features). But those complications permit simple programs. Brainfuck is simple, it has few complications (only 8 instructions) but this forces complications into the programs written in it.

Simplicity is not merely measured in the "number of things" you have, it's contextual. Otherwise we'd all be writing in binary representations because base two is the simplest way to communicate information.

Simplicity is also "how many things" it takes to express an idea or do a useful thing.

In many ways, written Chinese is simpler than written English.

This paper takes a deliberately weird solution to the problem.

FizzBuzz can be done more "normally" in Haskell with similar levels of simplicity as non-FP languages: https://wiki.haskell.org/Fizzbuzz

The paper even gives three normal examples early on, including one similar to the one in your link.

People who miss that are clearly looking hard for some reason to dismiss the work.

That is the FP version of the classic https://github.com/EnterpriseQualityCoding/FizzBuzzEnterpris... . One can overengineer stuff in both FP and OOP.
This looks very clear and straightforward even knowing nothing about Haskell syntax.
I found this to be well above average as an intro to Haskell.
plus you don't even need the first line.
https://stackoverflow.com/a/6957469

This seems very elegant and clean and I don’t know the slightest bit of Haskell other than “MONADS!!!”

I don't like that solution very much because of the 4 cases, or generally n*2 cases if there are n tokens. My favorite Haskell solution uses monoids (specificaly the Monoid instance of Maybe String) and is quite natural in Haskell but would take some explanation for non-Haskellers.

My version looks like the below. I didn't originate the approach but I like this spelling. It won't make total sense to non-Haskellers though.

    {-# LANGUAGE MonadComprehensions #-}
    import           Data.Maybe
    import           Data.Monoid

    fizz :: Int -> String
    fizz n = fromMaybe (show n) $ f 3 "Fizz" <> f 5 "Buzz"
      where
        f :: Int -> String -> Maybe String
        f d msg = [msg | n`rem`d == 0]

    main = mapM_ putStrLn [fizz n | n <- [1..100]]
This extends in an obvious way to FizzBuzzHissHowl, lets the Monoid instance manage concatenating the special tokens together while remembering if any of the rules have matched, and uses a monad comprehension on the Maybe monad to cleanly lift the token into the Maybe monad if the number is a multiple of whatever. The type annotation on f is added to make it a little bit clearer what is going on.

The use of monoids and the monad comprehension may be slightly flashy, but the use of Maybe to remember whether a rule has triggered is second nature in Haskell.

This is not too dissimilar to the final solution outlined in the essay, but is much more readable, in my opinion, due to its use of mappend over Maybe Strings instead of implementing the same sort of compositionality using regular function composition. Very elegant!
The one you linked is close to how you'd write this in idiomatic Haskell. The paper is describing a particular technique, and using FizzBuzz as an example problem, not suggesting that the technique is a good way to solve FizzBuzz.