views:

440

answers:

3

A while ago, I asked a question about $, and got useful answers -- in fact, I thought I understood how to use it.

It seems I was wrong :(

This example shows up in a tutorial:

instance Monad [] where
   xs >>= f = concat . map f $ xs

I can't for the life of me see why $ was used there; ghci isn't helping me either, as even tests I do there seem to show equivalence with the version that would simply omit the $. Can someone clarify this for me?

Thanks!

+11  A: 

The $ is used here because it has lower precedence than normal function application. Another way to write this code is like so:

instance Monad [] where
   xs >>= f = (concat . map f) xs

The idea here is to first construct a function (concat . map f) and then apply it to its argument (xs). As shown, this can also be done by simply putting parenthesis around the first part.

Note that omitting the $ in the original definition is not possible, it will result in a type error. This is because the function composition operator (the .) has a lower precedence than normal function application effectively turning the expression into:

instance Monad [] where
  xs >>= f = concat . (map f xs)

Which doesn't make sense, because the second argument to the function composition operator isn't a function at all. Although the following definition does make sense:

instance Monad [] where
  xs >>= f = concat (map f xs)

Incidentally, this is also the definition I would prefer, because it seems to me to be a lot clearer.

Tom Lokhorst
Thank you. I didn't realize that $ was even lower precedence than (.); I had been mentally parsing lines with (.) in it as separate "chunks," when this is not always the case! I agree that it would have been much clearer to write it how you did, or as: concat $ map f xs
J Cooper
I prefer "xs >>= f = concat $ map f xs" myself.
A. Rex
I do too, or in this case: `xs >>= f = concatMap f xs`
Tom Lokhorst
"xs >>= f = flip (join.fmap)" :)
Porges
To add to the noise: (>>=) = flip concatMap -- =)
Edward Kmett
+3  A: 

I'd like to explain why IMHO this is not the used style there:

instance Monad [] where
  xs >>= f = concat (map f xs)

concat . map f is an example of so-called pointfree-style writing; where pointfree means "without the point of application". Remember that in maths, in the expression y=f(x), we say that f is applied on the point x. In most cases, you can actually do a final step, replacing:

f x = something $ x

with

f = something

like f = concat . map f, and this is actually pointfree style. Which is clearer is arguable, but the pointfree style gives a different point of view which is also useful, so sometimes is used even when not exactly needed.

EDIT: I have replaced pointless with pointfree and fixed some examples, after the comment by Alasdair, whom I should thank.

Blaisorblade
I agree, pointless style can be useful, however I don't like using it when you actually do apply arguments to a function, as in this case.
Tom Lokhorst
Also, if you're not careful you can overdo it, here's some code I wrote yesterday ;-) `maybe id (((sp . ("string " ++)) .) . shows) mx`
Tom Lokhorst
'concat . map f $ xs" is not pointfree, bind for the list monad written in a pointfree style would be '(>>=) = flip concatMap' or similar. This is one example where the pointfree style is actually very clear. See http://www.haskell.org/haskellwiki/Pointfree
Alasdair
I know this is not point-free, but it is similar to pointfree style; since >>= has reversed arguments, flip is also required (I had forgot about the possibility to use `flip`, that's true).
Blaisorblade
+1  A: 

The reason $ is used here is doe to the type signature of (.):

(.) :: (b -> c) -> (a -> c) -> a -> c

Here we have

map f :: [a] -> [[b]]

and

concat :: [[b]] -> [b]

So we end up with

concat . map f :: [a] -> [b]

and the type of (.) could be written as

(.) :: ([[b]] -> [b]) -> ([a] -> [[b]]) -> [a] -> [b]

If we were to use concat . map f xs, we'd see that

map f xs :: [[b]]

And so cannot be used with (.). (the type would have to be (.) :: (a -> b) -> a -> b

Axman6