Hacker News new | ask | show | jobs
by caconym_ 3463 days ago
Your example can be written more clearly (IMO) in Haskell like this:

  sum $ map (^2) $ filter odd [1..10]
Or, defined as a function:

  > let foo = sum . map (^2) . filter odd
  > foo [1..10]
  165
You can write it without using the composition operator at all, if you prefer:

  > let bar xs = sum (map (^2) (filter odd xs))
  > bar [1..10]
  165
In general, you can write out your programs as crazy sequences of operators (whose precedence may not be obvious to the reader):

  > take 5 . unfoldr (Just . next) <$> getStdGen >>= putStrLn . show
  [1336079013,234736121,108049288,410228860,1573295749]
But you don't have to:

  > let infiniteRandoms = unfoldr (Just . next) :: StdGen -> [Int]
  > gen <- getStdGen
  > let fiveRandoms = take 5 (infiniteRandoms gen)
  > putStrLn (show fiveRandoms)
  [1336079013,234736121,108049288,410228860,1573295749]
People seem to think Haskell is all about writing your whole program in a single hideous line of code with incomprehensible sequences of arcane operators glued together by a glut of anonymous functions, but that's just bad code.

As for left-to-right vs right-to-left, I have never had a problem with the way Haskell does it. I think if you're coming from a language that does it differently, it makes sense that Haskell would trip you up in that regard. Haskell's composition operator matches the way it's usually written in mathematics, but there's nothing special about it and you could change it to go the other way if you really wanted to (but please don't):

  > let g . f = \x -> f (g x)
  > let foo = filter odd . map (^2) . sum
  > foo [1..10]
  165
`(>>>)` from `Control.Arrow` is a more reasonable way to get that sweet left-to-right action but using it might still throw people off:

  > let foo = filter odd >>> map (^2) >>> sum
  > foo [1..10]
  165
1 comments

To add to that from someone who went into programming after math grad school, composition is actually much more natural in the way to think about functions. When putting together functions, and where they map to, it is more natural to think about functions the way Haskell thinks about them, but more natural when writing them to go the other way at first. This is because when one is working at the lower level of just writing code it becomes easier to move from one data state to another, but at the high level, you lose some flexibility in seeing the high level structure and it becomes more difficult to figure out how to move things around if it turns out you have to modify the code.

In math, the preferred way of writing functions is via composition, as then it enables tools of visualization such as commutative diagrams. One thing to note is that in general mathematicians have a history of picking the right choice in notations for thinking about abstractions at an intuitive level in the world of mathematics (when there is a choice and it matters), and IMO they were correct about how they chose to represent function composition, although it is not immediately obvious unless you start talking about function domains and ranges - the composition notation matches the flow of reading how functions map values from one set to another.

That Clojure version reads like a complete mess to me personally.