21

I found precedence and associativity is a big obstacle for me to understand what the grammar is trying to express at first glance to haskell code.

For example,

blockyPlain :: Monad m => m t -> m t1 -> m (t, t1)
blockyPlain xs ys = xs >>= \x -> ys >>= \y -> return (x, y)

By experiment, I finally got it means,

blockyPlain xs ys = xs >>= (\x -> (ys >>= (\y -> return (x, y))))

instead of

blockyPlain xs ys = xs >>= (\x -> ys) >>= (\y -> return (x, y))

Which works as:

*Main> blockyPlain [1,2,3] [4,5,6]
[(1,4),(1,5),(1,6),(2,4),(2,5),(2,6),(3,4),(3,5),(3,6)]

I can get info from ghci for (>>=) as an operator, (infixl 1 >>=).

But there's no information for -> since it's not an operator.

Could someone of you guys give some reference to make this grammar thing easier to grasp?

ning
  • 731
  • 1
  • 4
  • 15
  • The [Haskell Report](http://www.haskell.org/onlinereport/haskell2010/haskellch3.html#x8-220003) is the definitive reference for Haskell syntax, though perhaps a BNF grammar is a bit low-level for the question you're asking...? – Daniel Wagner Jun 28 '12 at 02:09
  • I too struggle with precedence in Haskell and knowing when parens are appropriate and when they are superfluous. Any tutorials or guides other than the link above are welcomed. – wide_eyed_pupil May 26 '21 at 08:38

2 Answers2

26

The rule for lambdas is pretty simple: the body of the lambda extends as far to the right as possible without hitting an unbalanced parenthesis.

f (\x -> foo (bar baz) *** quux >>= quuxbar)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                       body
luqui
  • 59,485
  • 12
  • 145
  • 204
  • 3
    Which means it applies last. That is, (->) has a lower precedence then any other operator. I believe the same is true of `do` and (<-) as well they extend as far right as possible, apply last, and have a lower precedence then any operator. – John F. Miller Jun 28 '12 at 04:50
  • 5
    I really don't like the terms "first" and "last" for precendence -- I think it conflates grouping with evaluation order. It's something I run up against in math teaching. I like "tight" and "loose", thinking of the operators as holding on to their operands. Anyway, `->` isn't really an infix operator, since it does not connect two terms; rather, lambda is a syntactic form for an expression. That's why I didn't answer in terms of operators. – luqui Jun 28 '12 at 21:26
  • 1
    Both points well taken. "First" and "last" are the phrases used in most k-12 (and not a few undergrad) math curricula when talking about order of operations. My attempt was to apply those commonly used (though less accurate) terms to your very fine answer. – John F. Miller Jun 29 '12 at 05:18
7

A good rule of thumb seems to be that you can never make a custom operator that has precedence over built in syntactic constructs. For instance consider this example:

if b then f *** x else f *** y

Regardless of the associativity of ***, no one would expect it to binds as:

(if b then f *** x else f) *** y

There aren't a lot of syntactic constructs in Haskell (do and case are a little special because of layout syntax) but let can be used as another example:

(let x = y in y *** x) /= ((let x = y in y) *** x) 
Jonas Duregård
  • 917
  • 4
  • 8