It appears that in the context of that book, (@)
is being used as a stand-in for some binary operator such as (+)
or (<>)
, even though this is not actually legal Haskell syntax†.
For questions about Haskell syntax, it’s helpful to consult the Haskell 2010 Report.
In Ch. 2 Lexical Structure, under §2.2 Lexical Program Structure, you can find @
in the grammar of symbols that may appear in an operator name:
symbol → ascSymbol | uniSymbol⟨special | _
| "
| '
⟩
ascSymbol → !
| #
| $
| %
| &
| ⋆
| +
| .
| /
| <
| =
| >
| ?
| @
| \
| ^
| |
| -
| ~
| :
And §2.4 Lexical Structure: Identifiers and Operators defines valid operator names to include such symbols:
varsym → ( symbol⟨:
⟩ {symbol} )⟨reservedop | dashes⟩
consym → ( :
{symbol} )⟨reservedop⟩
However, the subscript in angle brackets denotes a “difference” or exclusion, so this disallows a list of reserved identifiers. Under the production reservedop you can find that @
appears in that list:
reservedop → ..
| :
| ::
| =
| \
| |
| <-
| ->
| @
| ~
| =>
The reason is that the @
symbol denotes an “as” pattern, described in §3.17.2.8 Informal Semantics of Pattern Matching:
- Matching an as-pattern var@apat against a value v is the result of matching apat against v, augmented with the binding of var to v.
As patterns are very useful for controlling the sharing of values and making certain definitions more concise, e.g.:
-- | Merge two sorted lists.
merge :: (Ord a) => [a] -> [a] -> [a]
-- ‘as0’ & ‘bs0’ denote the original input lists.
-- ‘as’ & ‘bs’ denote their tails.
merge as0@(a : as) bs0@(b : bs) = case compare a b of
-- ‘as0’ passes along the same list cell;
-- repeating ‘a : as’ would allocate a /new/ cell.
GT -> b : merge as0 bs
_ -> a : merge as bs0
merge [] bs0 = bs0
merge as0 [] = as0
Therefore, the definition:
x @ y = 0 `max` (x + y)
Or more conventionally written x@y = …
, is equivalent to defining two variables referring to the same value, which furthermore is defined recursively:
x = 0 `max` (x + y)
y = x
Without the type signature Int -> Int -> Int
, this definition would be accepted, defining x, y :: (Ord a, Num a) => a
, but attempting to evaluate either variable would produce an infinite loop, since this is “unproductive” recursion.
The solution is to use a non-reserved symbol as your operator name instead, such as <@>
or +.
.
† I can’t find a citation for this, but it’s possible that GHC used to accept this syntax, even though it was also disallowed by the Haskell 98 Report which was current at the time the first edition of this book came out, and that this example code just wasn’t updated for the second edition.