11

I come from Scala. So I frequently do stuff like:

println((1 to 10).filter(_ < 3).map(x => x*x))

In Haskell, after I discovered I can get rid of all the nested parenthesis using $ and ., I recently found myself writing:

putStrLn . show . map (**2) . filter (< 3) $ [1..10] 

Now, this works, but the code reads right-to-left, and unless I shift to Arabic, this is difficult for me to reason about.

Is there any other trick that makes me chain the functions from left to right? Or this is just the Haskell idiomatic way?

sscarduzio
  • 5,938
  • 5
  • 42
  • 54
  • 1
    Note that `putStrLn . show = print`. Also, you could use `mapM` or `forM`: `forM [x | x <- [1..10], x < 3] $ print . (**2)` or something like this. – Bakuriu Nov 08 '15 at 13:07
  • Once you get used to it, left to right is easier to reason about : it gives you the type of the expression. In your case `putStrnLn ...` I know it's an IO. – mb14 Nov 08 '15 at 18:12
  • 1
    Unhelpful observation: the 'flow' of ordinary chained function application (without using function composition) is right to left: `f . g . h $ x = f (g (h (x)))`. This is a very old convention (and holds even in C-like syntax, so it's not just a Haskellism). It's the dot-method syntax that is "backwards", not the function composition operator. Note that in your Scala example the 'flow' is much harder to trace; it actually starts in the middle at `(1 to 10)`, then moves to the right, then suddenly jumps back to the left to `println`. – Ben Nov 08 '15 at 22:18

3 Answers3

19

Unfortunately, it's the Haskell idiomatic way. But the & operator might do what you want.

import Data.Function ((&))

[1..10] & filter (< 3) & map (**2) & show & putStrLn

Essentially, (&) = flip ($). Likewise, Control.Arrow.(>>>) = flip (.)

UPDATE (6+ months later): I have to admit, this issue is a big source of frustration for me and I have been toying with this potential solution:

https://gist.github.com/obadz/9f322df8ba6c8a9767683d2f86af8589#file-directionalops-hs-L81

obadz
  • 889
  • 4
  • 10
  • 2
    Too bad `x & f >>> g` fails because of the wrong precedence... it would closely match `g . f $ x`. – chi Nov 08 '15 at 13:33
5

Yes, it is idiomatic Haskell. Not Arabic, but rather Mathematic, derived from the syntax for composition. See also Haskell composition (.) vs F#'s pipe forward operator (|>).

Still, even in Haskell you sometimes prefer to write your calls in the other direction, and you'll find a few libraries (e.g. Data.Function since base 4.8.0) who have defined

(&) = flip ($)

so that you can express your call as

[1..10] & filter (< 3) & map (**2) & show & putStrLn
Community
  • 1
  • 1
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • The only time I've personally missed a left-to-right chaining operator was [`fmap` in a monad](http://stackoverflow.com/q/20203056/1048572) however – Bergi Nov 08 '15 at 12:08
2

Why not make a new operator?

(#) :: a -> (a -> b) -> b
(#) = flip id

Now you can just write

[1..10] # filter (< 3) # map (**2) # show # putStrLn

This is the equivalent of the (&) operator from Data.Function.

chi
  • 111,837
  • 3
  • 133
  • 218
AJF
  • 11,767
  • 2
  • 37
  • 64