Hacker News new | ask | show | jobs
by fleshmonad 565 days ago
It's actually very readable once you get the hang of it. The transition from imperative paradigms to Haskell can be tough, but once you've overcome this barrier, the code reads effortlessly. In your example case: split the string into a list of words, that is tokenize based on spaces. Then map the read function onto this, which will parse each of the "words" in the list into some type. Annotations would likely be needed here. Then sum this list.

I much prefer this over 10 levels of class indirections or procedural style.

3 comments

Isn't it the case that anything is very readable once you get the hang of it? I think the problem is exactly in the "get the hang of it" part. For some languages, that's really difficult, while for others it's almost trivial. From my own experience, Haskell is very very difficult to "get the hang of" despite my multiple attempts, while something like Javascript is trivial for me (interestingly enough, except when it uses too much of the new FP patterns!) even when I do not program on it daily.
This is like complaining that Spanish is so hard to learn while English is actually quite intuitive. Of course it’s easier to understand what you already know.
Not at all. I tried to make the point of distinction clear by saying I do not use JS, nor Haskell, daily, but JS is more readable, without a doubt, so it's more like saying something like "english is more readable than french to a spanish speaker" (the analogy makes much less sense, but trying to correct yours). I think that we can agree that everyone's a priori is what they know in the world, which is common language... and everyone is familiar with "recipes", or step-by-step instructions... which is the same as imperative code, not functional.

Don't get me wrong, I like FP and have been trying to get into it for a long time. But currently I strongly believe FP as commonly done in Haskell is just too far from what we expect even before we start writing code. Combining functions and chaining Monads just seems to me to be extremely hard to do and understand, and I don't need to do any of that in "lesser" languages. However, I am finally "getting it" with newer languages like Flix and Unison - they let me just use `let` and stuff like that which makes the code trivial again, while being basically purely functional.

> JS is more readable, without a doubt

In what sense?

Haskell's: strSum = sum . map read . words

in JS would be: const strSum = str => str.split(' ').map(Number).reduce((a, b) => a + b, 0);

for a person who's not already a JS programmer, the first one would be more readable (without a doubt), it literally reads like plain English: "sum of mapped read of words".

Haskell's version is more "mathematical" and straightforward. Each function has one clear purpose. The composition operator clearly shows data transformation flow. No hidden behavior or type coercion surprises.

Whereas JS version requires knowledge of:

- How Number works as a function vs constructor

- Implicit type coercion rules

- Method chaining syntax

- reduce()'s callback syntax and initial value

- How split() handles edge cases

So while the JS code might look familiar, it actually requires more background knowledge and consideration of implementation details to fully understand its behavior. Javascript is far more complex language than most programmers realize. btw, I myself don't write Haskell, but deal with Javascript almost daily and I just can't agree that JS is "more readable" than many other PLs. With Typescript it gets even more "eye-hurting".

> in JS would be: const strSum = str => str.split(' ').map(Number).reduce((a, b) => a + b, 0);

It's funny to me that you quote the FP-like version of that in JS.

The more traditional version would be more like this:

    function strSum(str) {
        let words = str.split(' ');
        let sum = 0;
        for (word of words) {
            sum += new Number(word);
        }
        return sum;
    }
I do sincerely think this is more readable, no matter your background. It splits the steps more clearly. Doesn't require you to keep almost anything in your head as you read. It looks stupid, which is great! Anyone no matter how stupid can read this as long as they've had any programming experience, in any language. I would bet someone who only ever learned Haskell would understand this without ever seeing a procedural language before.
I don't even know where to start, your biases here are so explicit.

- The assumption that "verbose = readable" and "explicit loops = clearer"? Seriously?

- The suggestion that "looking stupid" is somehow a virtue in code? "Simple" I can still buy, but "stupid"... really?

- You're using new Number() - which is actually wrong - it creates a Number object, not a primitive;

- You `sum +=` is doing not a single op but multiple things implicitly: addition, assignment, potential type coercion, mutation of state;

- for loops are another layer of complexity - iterator protocol implementation, mutable loop counter management, scoping issues, potential off-by-one errors, break/continue possibilities, possible loop var shadowing, etc. Even though for..of is Javascript's attempt at a more FP-style iteration pattern and is safer than the indexed loop.

You clearly underestimate how natural functional concepts can be - composition is a fundamental concept we use daily (like "wash then dry" vs "first get a towel, then turn on water, then...").

Your "simple" imperative version actually requires understanding more concepts and implicit behaviors than the functional version! The irony is that while you're trying to argue for simplicity, you're choosing an approach with more hidden complexity.

Again, I'm not huge fan of Haskell, yet, the Haskell version has:

- No hidden operations

- No mutation

- Clear, single-purpose functions

- Explicit data flow

You have just demonstrated several key benefits of functional programming and why anyone who writes code should try learning languages like Haskell, Clojure, Elixir, etc., even though practical benefits may not be obvious at first.

> Haskell is just too far from what we expect

Who's "we"?

I spent years writing JavaScript, PHP, and Ruby. I thought Haskell was weird and hard, and probably not practical in the real world.

As it turned out, I was just being a fool. Once you actually learn it, you realise how silly the opinions are that you had of it before you learned it.

Advent of Code is running right now. Why don't you just try learning the language?

I learned the language more than 10 years ago. No, it's not for me. Please don't assume that because somebody doesn't find Haskell readable the person must be ignorant.
> it's more like saying something like "english is more readable than french to a spanish speaker"

On the contrary French is much more readable than English to a Spanish speaker. Because French is much more similar to Spanish than English is.

Same with your JS example, I would guess it is much more similar to what you are used to

Maybe „French is more readable than Korean” would be a better analogy. Sure, if you’re already familiar with the Latin alphabet, but Hangul is clearly a better writing system.
> I do not use JS, nor Haskell, daily, but JS is more readable

I’m guessing you do use languages that are very similar to JS. Like a Spanish speaker saying “I don’t speak Italian or Chinese but Italian is way easier.” If you wrote F# every day you would probably find Haskell syntax quite intuitive

I was trying to make the point that no, it's not that at all. But I guess it's a very hard point to make and even though I am convinced that I'm right and this has nothing to do with familiarity, I can't find any serious research showing either way.

I know a dozen languages well. Everyone here thinking it's just ignorance, but that's not the case. There's just no way that, for me, Haskell and similar languages are readable in any sense just because they're more concise. If that was the case Haskell still wouldn't be close to the most readable, but something like APL or Forth would. I've tried for more than 10 years to be like you guys and read a bunch of function compositions without any variable names to be seen, a few monadic operators and think "wow so easy to read"... but no, it's still completely unreadable to me. I guess I am much more a Go person than a Haskell person, and I am happy about that.

It is a point of familiarity. Just because you've been coding in multiple languages before doesn't necessarily make you "a better programmer" (in the sense that you've developed good instincts to quickly mentally parse different pieces of code) - you could have been using programming languages of similar paradigms. It took me a few months of writing Clojure (niche language) to start seeing things in a different light - I also, just like you, used to think that imperative constructs are more readable and easier to reason about. I was wrong.

There's no such thing as a "Go person" or a "Haskell person"; all programming languages are made up. Nobody has "more natural inclination" for coding one way than another. Just get your ass out of the comfort zone, try learning a new (to you) language - give it a heartfelt attempt to use it - that may change your life.

Just to be clear - I'm not saying Haskell is easy, oh no, not at all. I'm just saying that it stops being so intimidating and weird after a while.

> and everyone is familiar with "recipes", or step-by-step instructions... which is the same as imperative code, not functional.

everyone is familiar with "I don't know how exactly, but generally it would be this way..., we can discuss specifics later" which is the same as reading the above pointfree notation (sum . map read . words) verbatim instead of imperatively inside-out: something is a sum of all parsed values of space-separated words.

Yes but js is very similar to other languages you know.
It's a matter of "density".

The more dense the code, the more there is to unpack until you are deep in the language.

It's OK for languages to be more verbose and offer structural cues (braces) as this often helps in human parsing of logic.

> In your example case: split the string into a list of words, that is tokenize based on spaces.

You've made a common mistake. You're wiring your listener's thinking with the imperative inside-out approach that you're used to. Instead, it should be explained as this: "strSum = sum . map read . words" is "a sum of all parsed values of the original input of space-separated words". The reason you should avoid inside-out explanations is because in Haskell you're allowed to move from general ideas to specifics, and you can sprinkle `undefined` and `_` for specific details whilst thinking about general ideas and interfaces.

I really like the FP paradigm, but could you all stop using weird abbreviations and random characters as substitute for operations?

You don't do programming with chalk on a wallboard, for crying out loud. Ideally, you are using a good IDE with syntax completion. Therefore, readability matters more than the ability to bang out commands in as few keystrokes as possible.

Iverson's Notation as a Tool of Thought defends the opposite idea (and explains the reason for APL): https://news.ycombinator.com/item?id=25249563

It's about phase transitions. When you understand the system, shorter symbols are easier/faster to reason with. If your primitives are well thought out for the domain, this notation will be the optimal way of understanding it!

On the other hand, longer names help on board new people. Theoretically, you could avoid this issue by transforming back and forth. Uiua e.g. lets you enter symbols by typing out their names. Typing "sum = reduce add" becomes: "sum ← /+". If you transform it back...

Imagine if you could encode the std lib with aliases!

I've originally studied social sciences, so my training uses maths but its core is words. I dislike symbol only notation. Real words trigger different parts of my brain. I am very good at memorizing content and flow of texts but bad at keeping symbols in my head. I have the same issue with language, e.g. I have no problem with pinyin, but written Chinese characters are taxing me.
I second this, most programmers seem to be fixated on the idea that all code should show at every moment how data types and their values are being passed and tossed around, and they simply ignore or refuse to realise that you can omit it and think in terms of functions fitting the slots.