6 ms·
Maybe because I haven’t used languages like these in the past, but I hardly think this is elegant, much read readale. I would hate my life trying to parse code
by bhargav 2y ago
Maybe because I haven’t used languages like these in the past, but I hardly think this is elegant, much read readale. I would hate my life trying to parse code like this in a 10k LOC codebase.
strSum = sum . map read . words
- solomonb 2y agoIn a production application you generally don't write code like that. I find it tends to be the opposite problem where you often see giant `do` blocks performing all sorts of monadic effects.
- djur 2y agoYou especially wouldn't use `read`.
- fleshmonad 2y agoIt'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.
- brabel 2y agoIsn'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.
- kubb 2y agoThis 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.
- brabel 2y agoNot 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.
- yakshaving_jgt 2y ago> 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?
- brabel 2y agoI 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.
- HdS84 2y agoI 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.
- veqq 2y agoIverson's Notation as a Tool of Thought defends the opposite idea (and explains the reason for APL): https://news.ycombinator.com/item?id=25249563 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!
- instig007 2y agoI 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.
- HdS84 2y agoI'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.
- instig007 2y ago> 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.
- djur 2y agoIf you're familiar with Haskell, this is something you can just look at and parse without thinking. It's all basic Haskell syntax and concepts (function composition and partial application). I haven't touched Haskell for a few years and I didn't have any trouble interpreting it as "strSum is a function that takes a single string argument, splits it by whitespace, interprets each chunk as a number, and returns the sum".
- devjab 2y agoI’m not familiar with Haskell and I could read almost all of that from it. No idea how you can tell it splits on white space though.
- tikhonj 2y agoYeah, that's where you just have to know what the "words" function from the standard library does.
- dawidloubser 2y agoI guess the more succinct the code, the more the reliance on understanding what a function actually does - either through experience, or by reading the docs. The words function is simply: words :: String -> [String] So that words "foo bar baz" -- Produces: ["foo","bar","baz"] In my experience, both the blessing and the curse of Haskell's incredible succinct expressiveness is that, like other specialised languages - for example using latin for succinctly-expressed legal terms - you need a strong understanding of the language and its standard library - similar to the library of commonly used "legal terms" in law circles - to participate meaningfully. Haskell, and languages like Go (which anybody with a bit of programming experience can easily follow) have very different goals. Like many in this discussion, I too have developed a love/hate thing with Haskell. But boy, are the good parts good ...
- devjab 2y agoI recently learned that things like Goroutines aren’t naturally written with buffers and channels. Granted anyone who reads the original documentation would likely do it correctly, but apparently that’s not how they are intuitively written. so while it may be easy to read it might be harder to write than I was assuming. So maybe there a difference where Haskell has an advantage? I mentioned it in my previous comment but I don’t know Haskell at all, but if this is “the way” to do splits by word then you’ll know both to read and write it. Which would be a strength on its own, since I imagine it would be kind of hard to do wrong since you’ll need that Haskell understanding in the first place.
- runeks 2y ago> Maybe because I haven’t used languages like these in the past [...] Yes, that definitely the case. If you know what each function above does, including the function composition dot (.), then this is like reading English — assuming you know how to read English.
- bhargav 2y agoThe “map read” part is what’s off. I think it’s because parens are optional or not required. There are other languages which are functional as well. like the one in the article and like Elixir where readability is it sacrificed. I still think readability is atrocious in this language. Sure I can get used to it, but I’d never want to subject myself to that
- instig007 2y agoProblem solved: strSum = sum . parsed . words where parsed = map read
- itishappy 2y agoParenthesis are not really optional, they're just used differently than other languages. Other languages use parenthesis for function application and grouping, in Haskell it's just grouping. wordsPerLine = filter (>0) . map (length . words) . lines Funnily enough, parenthesis are actually optional in Elixir, although it's a warning to use pipe syntax without them. The following is valid in both Haskell and Elixer: length [1,2,3]
- runeks 2y agoWhenever you see something like apply foo bar (lol wat) in Haskell, and it confuses you, simply mentally replace it with apply(foo, bar, lol(wat)) To translate into a more popular syntax (e.g. JavaScript).
- worksonmymach 2y ago1. Change . to | 2. Reverse Now you have: words | map read | sum Or.. $ cat words | map -e read | sum
- tromp 2y agoWould you propose the same change for nested function calls y = f(g(h(x))), changing it into y = x | h | g | f ?
- worksonmymach 2y agoIt can look nice. It is like asking: passive or active voice? It depends what you are writing and what makes sense to the reader. I do like pipelines though!
- bhargav 2y agoYes but the notation of dot, plus such function names plus optional parens makes it sure read like English. That’s great but it’ll be a nightmare when you are also dealing with strings which similar English in it.
- kreyenborgi 2y ago|> is a common way of writing the pipe in haskell, so words |> map read |> sum IHP uses it a lot.
- elbear 2y agoYou can see it as a pipeline where the output of the right-most function is plugged into the input of the function to its left.
- Symmetry 2y agoCompare it to a bashism like find . -name '*.py' | sed 's/.*/"&"/' | xargs wc -l But instead of using | to tie the different functions together you're using . and the order is reversed.
- epgui 2y agoIt’s not inelegant, it’s just unfamiliar to you.
- 59nadir 2y agoIn our codebase we enforced usage of `>>>` instead, which composes forward instead of backwards: strSum = words >>> map read >>> sum For most people this then becomes "Apply `words` to the input argument, pass the result to `map read` and then `sum` the results of that". I don't think `.` is super complex to read and parse, but we had people new to Haskell so I thought it prudent to start them off just with `>>>` and keep it that way. Most things are read left-to-right and top-to-bottom in a codebase otherwise so I don't see why not. Edit: I also told everyone it's fine to just spell out your arguments: stringSum sentence = sentence & words & map read & sum In the example above `&` is just your average pipe-operator. Not currying when you don't have to is also fine, and will actually improve performance in certain scenarios. Edit 2: The truth is that there are way more important things to talk about in a production code base than currying, and people not using currying very much wouldn't be an issue; but they'll have to understand different pointer/reference types, the `ReaderT` monad (transformer), etc., and how a `ReaderT env IO` stack works and why it's going to be better than whatever nonsense theoretical stack with many layers and transformers that can be thought up. Once you've taught them `ReaderT env IO` and pointer types (maybe including `TVar`s) you're up and running and can write pretty sophisticated multi-threaded, safe production code.