DRAFT
This is a work in progress, not an official release of the Haskell 2010 Revised Language Report.
prev Contents next

10 Syntax Reference

10.1 Notational Conventions

These notational conventions are used for presenting syntax:

[pattern]optional
{pattern}zero or more repetitions
(pattern)grouping
pat₁ | patβ‚‚choice
π‘π‘Žπ‘‘βŸ¨π‘π‘Žπ‘‘'⟩differenceβ€”elements generated by pat except those generated by pat'
πšπš’πš‹πš˜πš—πšŠπšŒπšŒπš’terminal syntax in typewriter font

BNF-like syntax is used throughout, with productions having the form:

π‘›π‘œπ‘›π‘‘π‘’π‘Ÿπ‘šβ†’π‘Žπ‘™π‘‘β‚|π‘Žπ‘™π‘‘β‚‚|…|π‘Žπ‘™π‘‘β‚™

In both the lexical and the context-free syntax, there are some ambiguities that are to be resolved by making grammatical phrases as long as possible, proceeding from left to right (in shift-reduce parsing, resolving shift/reduce conflicts by shifting). In the lexical syntax, this is the β€œmaximal munch” rule. In the context-free syntax, this means that conditionals, let-expressions, and lambda abstractions extend to the right as far as possible.

10.2 Lexical Syntax

π‘π‘Ÿπ‘œπ‘”π‘Ÿπ‘Žπ‘šβ†’{𝑙𝑒π‘₯π‘’π‘šπ‘’|π‘€β„Žπ‘–π‘‘π‘’π‘ π‘π‘Žπ‘π‘’}
𝑙𝑒π‘₯π‘’π‘šπ‘’β†’π‘žπ‘£π‘Žπ‘Ÿπ‘–π‘‘|π‘žπ‘π‘œπ‘›π‘–π‘‘|π‘žπ‘£π‘Žπ‘Ÿπ‘ π‘¦π‘š|π‘žπ‘π‘œπ‘›π‘ π‘¦π‘š
|π‘™π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘™|π‘ π‘π‘’π‘π‘–π‘Žπ‘™|π‘Ÿπ‘’π‘ π‘’π‘Ÿπ‘£π‘’π‘‘π‘œπ‘|π‘Ÿπ‘’π‘ π‘’π‘Ÿπ‘£π‘’π‘‘π‘–π‘‘
π‘™π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘™β†’π‘–π‘›π‘‘π‘’π‘”π‘’π‘Ÿ|π‘“π‘™π‘œπ‘Žπ‘‘|π‘β„Žπ‘Žπ‘Ÿ|π‘ π‘‘π‘Ÿπ‘–π‘›π‘”
π‘ π‘π‘’π‘π‘–π‘Žπ‘™β†’(|)|,|;|[|]|`|{|}
π‘€β„Žπ‘–π‘‘π‘’π‘ π‘π‘Žπ‘π‘’β†’π‘€β„Žπ‘–π‘‘π‘’π‘ π‘‘π‘’π‘“π‘“{π‘€β„Žπ‘–π‘‘π‘’π‘ π‘‘π‘’π‘“π‘“}
π‘€β„Žπ‘–π‘‘π‘’π‘ π‘‘π‘’π‘“π‘“β†’π‘€β„Žπ‘–π‘‘π‘’π‘β„Žπ‘Žπ‘Ÿ|π‘π‘œπ‘šπ‘šπ‘’π‘›π‘‘|π‘›π‘π‘œπ‘šπ‘šπ‘’π‘›π‘‘
π‘€β„Žπ‘–π‘‘π‘’π‘β„Žπ‘Žπ‘Ÿβ†’π‘›π‘’π‘€π‘™π‘–π‘›π‘’|π‘£π‘’π‘Ÿπ‘‘π‘Žπ‘|π‘ π‘π‘Žπ‘π‘’|π‘‘π‘Žπ‘|π‘’π‘›π‘–π‘Šβ„Žπ‘–π‘‘π‘’
π‘›π‘’π‘€π‘™π‘–π‘›π‘’β†’π‘Ÿπ‘’π‘‘π‘’π‘Ÿπ‘›π‘™π‘–π‘›π‘’π‘“π‘’π‘’π‘‘|π‘Ÿπ‘’π‘‘π‘’π‘Ÿπ‘›|𝑙𝑖𝑛𝑒𝑓𝑒𝑒𝑑|π‘“π‘œπ‘Ÿπ‘šπ‘“π‘’π‘’π‘‘
π‘Ÿπ‘’π‘‘π‘’π‘Ÿπ‘›β†’a carriage return
𝑙𝑖𝑛𝑒𝑓𝑒𝑒𝑑→a line feed
π‘£π‘’π‘Ÿπ‘‘π‘Žπ‘β†’a vertical tab
π‘“π‘œπ‘Ÿπ‘šπ‘“π‘’π‘’π‘‘β†’a form feed
π‘ π‘π‘Žπ‘π‘’β†’a space
π‘‘π‘Žπ‘β†’a horizontal tab
π‘’π‘›π‘–π‘Šβ„Žπ‘–π‘‘π‘’β†’any Unicode character defined as whitespace
π‘π‘œπ‘šπ‘šπ‘’π‘›π‘‘β†’π‘‘π‘Žπ‘ β„Žπ‘’π‘ [π‘Žπ‘›π‘¦βŸ¨π‘ π‘¦π‘šπ‘π‘œπ‘™βŸ©{π‘Žπ‘›π‘¦}]𝑛𝑒𝑀𝑙𝑖𝑛𝑒
π‘‘π‘Žπ‘ β„Žπ‘’π‘ β†’--{-}
π‘œπ‘π‘’π‘›π‘π‘œπ‘šβ†’{-
π‘π‘™π‘œπ‘ π‘’π‘π‘œπ‘šβ†’-}
π‘›π‘π‘œπ‘šπ‘šπ‘’π‘›π‘‘β†’π‘œπ‘π‘’π‘›π‘π‘œπ‘šπ΄π‘π‘Œπ‘ π‘’π‘ž{π‘›π‘π‘œπ‘šπ‘šπ‘’π‘›π‘‘π΄π‘π‘Œπ‘ π‘’π‘ž}π‘π‘™π‘œπ‘ π‘’π‘π‘œπ‘š
π΄π‘π‘Œπ‘ π‘’π‘žβ†’{π΄π‘π‘Œ}⟨{π΄π‘π‘Œ}(π‘œπ‘π‘’π‘›π‘π‘œπ‘š|π‘π‘™π‘œπ‘ π‘’π‘π‘œπ‘š){π΄π‘π‘Œ}⟩
π΄π‘π‘Œβ†’π‘”π‘Ÿπ‘Žπ‘β„Žπ‘–π‘|π‘€β„Žπ‘–π‘‘π‘’π‘β„Žπ‘Žπ‘Ÿ
π‘Žπ‘›π‘¦β†’π‘”π‘Ÿπ‘Žπ‘β„Žπ‘–π‘|π‘ π‘π‘Žπ‘π‘’|π‘‘π‘Žπ‘
π‘”π‘Ÿπ‘Žπ‘β„Žπ‘–π‘β†’π‘ π‘šπ‘Žπ‘™π‘™|π‘™π‘Žπ‘Ÿπ‘”π‘’|π‘ π‘¦π‘šπ‘π‘œπ‘™|𝑑𝑖𝑔𝑖𝑑|π‘ π‘π‘’π‘π‘–π‘Žπ‘™|"|'
π‘ π‘šπ‘Žπ‘™π‘™β†’π‘Žπ‘ π‘π‘†π‘šπ‘Žπ‘™π‘™|π‘’π‘›π‘–π‘†π‘šπ‘Žπ‘™π‘™|_
π‘Žπ‘ π‘π‘†π‘šπ‘Žπ‘™π‘™β†’πšŠ|πš‹|…|𝚣
π‘’π‘›π‘–π‘†π‘šπ‘Žπ‘™π‘™β†’any Unicode lowercase letter
π‘™π‘Žπ‘Ÿπ‘”π‘’β†’π‘Žπ‘ π‘πΏπ‘Žπ‘Ÿπ‘”π‘’|π‘’π‘›π‘–πΏπ‘Žπ‘Ÿπ‘”π‘’
π‘Žπ‘ π‘πΏπ‘Žπ‘Ÿπ‘”π‘’β†’π™°|𝙱|…|πš‰
π‘’π‘›π‘–πΏπ‘Žπ‘Ÿπ‘”π‘’β†’any uppercase or titlecase Unicode letter
π‘ π‘¦π‘šπ‘π‘œπ‘™β†’π‘Žπ‘ π‘π‘†π‘¦π‘šπ‘π‘œπ‘™|π‘’π‘›π‘–π‘†π‘¦π‘šπ‘π‘œπ‘™βŸ¨π‘ π‘π‘’π‘π‘–π‘Žπ‘™|_|"|'⟩
π‘Žπ‘ π‘π‘†π‘¦π‘šπ‘π‘œπ‘™β†’!|#|$|%|&|*|+|.|/|<|=|>|?|@
|\|^|||-|~|:
π‘’π‘›π‘–π‘†π‘¦π‘šπ‘π‘œπ‘™β†’any Unicode symbol or punctuation
π‘‘π‘–π‘”π‘–π‘‘β†’π‘Žπ‘ π‘π·π‘–π‘”π‘–π‘‘|𝑒𝑛𝑖𝐷𝑖𝑔𝑖𝑑
π‘Žπ‘ π‘π·π‘–π‘”π‘–π‘‘β†’πŸΆ|𝟷|…|𝟿
𝑒𝑛𝑖𝐷𝑖𝑔𝑖𝑑→any Unicode decimal digit
π‘œπ‘π‘‘π‘–π‘‘β†’πŸΆ|𝟷|…|𝟽
β„Žπ‘’π‘₯π‘–π‘‘β†’π‘Žπ‘ π‘π·π‘–π‘”π‘–π‘‘|𝙰|…|𝙡|𝚊|…|𝚏
π‘£π‘Žπ‘Ÿπ‘–π‘‘β†’(π‘ π‘šπ‘Žπ‘™π‘™{π‘ π‘šπ‘Žπ‘™π‘™|π‘™π‘Žπ‘Ÿπ‘”π‘’|𝑑𝑖𝑔𝑖𝑑|'})βŸ¨π‘Ÿπ‘’π‘ π‘’π‘Ÿπ‘£π‘’π‘‘π‘–π‘‘βŸ©
π‘π‘œπ‘›π‘–π‘‘β†’π‘™π‘Žπ‘Ÿπ‘”π‘’{π‘ π‘šπ‘Žπ‘™π‘™|π‘™π‘Žπ‘Ÿπ‘”π‘’|𝑑𝑖𝑔𝑖𝑑|'}
π‘Ÿπ‘’π‘ π‘’π‘Ÿπ‘£π‘’π‘‘π‘–π‘‘β†’πšŒπšŠπšœπšŽ|πšŒπš•πšŠπšœπšœ|𝚍𝚊𝚝𝚊|πšπšŽπšπšŠπšžπš•πš|πšπšŽπš›πš’πšŸπš’πš—πš|𝚍𝚘|πšŽπš•πšœπšŽ
|πšπš˜πš›πšŠπš•πš•|πšπš˜πš›πšŽπš’πšπš—|πš’πš|πš’πš–πš™πš˜πš›πš|πš’πš—|πš’πš—πšπš’πš‘|πš’πš—πšπš’πš‘πš•
|πš’πš—πšπš’πš‘πš›|πš’πš—πšœπšπšŠπš—πšŒπšŽ|πš•πšŽπš|πš–πš˜πšπšžπš•πšŽ|πš—πšŽπš πšπš’πš™πšŽ|𝚘𝚏
|πšπš‘πšŽπš—|πšπš’πš™πšŽ|πš πš‘πšŽπš›πšŽ|_
π‘£π‘Žπ‘Ÿπ‘ π‘¦π‘šβ†’(π‘ π‘¦π‘šπ‘π‘œπ‘™βŸ¨:⟩{π‘ π‘¦π‘šπ‘π‘œπ‘™})βŸ¨π‘Ÿπ‘’π‘ π‘’π‘Ÿπ‘£π‘’π‘‘π‘œπ‘|π‘‘π‘Žπ‘ β„Žπ‘’π‘ βŸ©
π‘π‘œπ‘›π‘ π‘¦π‘šβ†’(:{π‘ π‘¦π‘šπ‘π‘œπ‘™})βŸ¨π‘Ÿπ‘’π‘ π‘’π‘Ÿπ‘£π‘’π‘‘π‘œπ‘βŸ©
π‘Ÿπ‘’π‘ π‘’π‘Ÿπ‘£π‘’π‘‘π‘œπ‘β†’..|:|::|=|\|||<-|->|@|~|=>
π‘£π‘Žπ‘Ÿπ‘–π‘‘(variables)
π‘π‘œπ‘›π‘–π‘‘(constructors)
π‘‘π‘¦π‘£π‘Žπ‘Ÿβ†’π‘£π‘Žπ‘Ÿπ‘–π‘‘(type variables)
π‘‘π‘¦π‘π‘œπ‘›β†’π‘π‘œπ‘›π‘–π‘‘(type constructors)
π‘‘π‘¦π‘π‘™π‘ β†’π‘π‘œπ‘›π‘–π‘‘(type classes)
π‘šπ‘œπ‘‘π‘–π‘‘β†’{π‘π‘œπ‘›π‘–π‘‘.}π‘π‘œπ‘›π‘–π‘‘(modules)
π‘žπ‘£π‘Žπ‘Ÿπ‘–π‘‘β†’[π‘šπ‘œπ‘‘π‘–π‘‘.]π‘£π‘Žπ‘Ÿπ‘–π‘‘
π‘žπ‘π‘œπ‘›π‘–π‘‘β†’[π‘šπ‘œπ‘‘π‘–π‘‘.]π‘π‘œπ‘›π‘–π‘‘
π‘žπ‘‘π‘¦π‘π‘œπ‘›β†’[π‘šπ‘œπ‘‘π‘–π‘‘.]π‘‘π‘¦π‘π‘œπ‘›
π‘žπ‘‘π‘¦π‘π‘™π‘ β†’[π‘šπ‘œπ‘‘π‘–π‘‘.]𝑑𝑦𝑐𝑙𝑠
π‘žπ‘£π‘Žπ‘Ÿπ‘ π‘¦π‘šβ†’[π‘šπ‘œπ‘‘π‘–π‘‘.]π‘£π‘Žπ‘Ÿπ‘ π‘¦π‘š
π‘žπ‘π‘œπ‘›π‘ π‘¦π‘šβ†’[π‘šπ‘œπ‘‘π‘–π‘‘.]π‘π‘œπ‘›π‘ π‘¦π‘š
π‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™β†’π‘Žπ‘ π‘π·π‘–π‘”π‘–π‘‘{π‘Žπ‘ π‘π·π‘–π‘”π‘–π‘‘}
π‘œπ‘π‘‘π‘Žπ‘™β†’π‘œπ‘π‘‘π‘–π‘‘{π‘œπ‘π‘‘π‘–π‘‘}
β„Žπ‘’π‘₯π‘Žπ‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™β†’β„Žπ‘’π‘₯𝑖𝑑{β„Žπ‘’π‘₯𝑖𝑑}
π‘–π‘›π‘‘π‘’π‘”π‘’π‘Ÿβ†’π‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™
|πŸΆπš˜π‘œπ‘π‘‘π‘Žπ‘™|πŸΆπ™Ύπ‘œπ‘π‘‘π‘Žπ‘™
|πŸΆπš‘β„Žπ‘’π‘₯π‘Žπ‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™|πŸΆπš‡β„Žπ‘’π‘₯π‘Žπ‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™
π‘“π‘™π‘œπ‘Žπ‘‘β†’π‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™.π‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™[𝑒π‘₯π‘π‘œπ‘›π‘’π‘›π‘‘]
|π‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™π‘’π‘₯π‘π‘œπ‘›π‘’π‘›π‘‘
𝑒π‘₯π‘π‘œπ‘›π‘’π‘›π‘‘β†’(𝚎|𝙴)[+|-]π‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™
π‘β„Žπ‘Žπ‘Ÿβ†’'(π‘”π‘Ÿπ‘Žπ‘β„Žπ‘–π‘βŸ¨'|\⟩|π‘ π‘π‘Žπ‘π‘’|π‘’π‘ π‘π‘Žπ‘π‘’βŸ¨\&⟩)'
π‘ π‘‘π‘Ÿπ‘–π‘›π‘”β†’"{π‘”π‘Ÿπ‘Žπ‘β„Žπ‘–π‘βŸ¨"|\⟩|π‘ π‘π‘Žπ‘π‘’|π‘’π‘ π‘π‘Žπ‘π‘’|π‘”π‘Žπ‘}"
π‘’π‘ π‘π‘Žπ‘π‘’β†’\(π‘β„Žπ‘Žπ‘Ÿπ‘’π‘ π‘|π‘Žπ‘ π‘π‘–π‘–|π‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™|πš˜π‘œπ‘π‘‘π‘Žπ‘™|πš‘β„Žπ‘’π‘₯π‘Žπ‘‘π‘’π‘π‘–π‘šπ‘Žπ‘™)
π‘β„Žπ‘Žπ‘Ÿπ‘’π‘ π‘β†’πšŠ|πš‹|𝚏|πš—|πš›|𝚝|𝚟|\|"|'|&
π‘Žπ‘ π‘π‘–π‘–β†’^π‘π‘›π‘‘π‘Ÿπ‘™|π™½πš„π™»|πš‚π™Ύπ™·|πš‚πšƒπš‡|π™΄πšƒπš‡|π™΄π™Ύπšƒ|π™΄π™½πš€|𝙰𝙲𝙺
|𝙱𝙴𝙻|π™±πš‚|π™·πšƒ|𝙻𝙡|πš…πšƒ|𝙡𝙡|π™²πš|πš‚π™Ύ|πš‚π™Έ|𝙳𝙻𝙴
|π™³π™²πŸ·|π™³π™²πŸΈ|π™³π™²πŸΉ|π™³π™²πŸΊ|𝙽𝙰𝙺|πš‚πšˆπ™½|π™΄πšƒπ™±|𝙲𝙰𝙽
|𝙴𝙼|πš‚πš„π™±|π™΄πš‚π™²|π™΅πš‚|π™Άπš‚|πšπš‚|πš„πš‚|πš‚π™Ώ|𝙳𝙴𝙻
π‘π‘›π‘‘π‘Ÿπ‘™β†’π‘Žπ‘ π‘πΏπ‘Žπ‘Ÿπ‘”π‘’|@|[|\|]|^|_
π‘”π‘Žπ‘β†’\π‘€β„Žπ‘–π‘‘π‘’π‘β„Žπ‘Žπ‘Ÿ{π‘€β„Žπ‘–π‘‘π‘’π‘β„Žπ‘Žπ‘Ÿ}\

10.3 Layout

SectionΒ 2.7 gives an informal discussion of the layout rule. This section defines it more precisely.

The meaning of a Haskell program may depend on its layout. The effect of layout on its meaning can be completely described by adding braces and semicolons in places determined by the layout. The meaning of this augmented program is now layout insensitive.

The effect of layout is specified in this section by describing how to add braces and semicolons to a laid-out program. The specification takes the form of a function 𝐿 that performs the translation. The input to 𝐿 is:

The β€œindentation” of a lexeme is the column number of the first character of that lexeme; the indentation of a line is the indentation of its leftmost lexeme. To determine the column number, assume a fixed-width font with the following conventions:

For the purposes of the layout rule, Unicode characters in a source program are considered to be of the same, fixed, width as an ASCII character. However, to avoid visual confusion, programmers should avoid writing programs in which the meaning of implicit layout depends on the width of non-space characters.

The application

L tokens []

delivers a layout-insensitive translation of π‘‘π‘œπ‘˜π‘’π‘›π‘ , where π‘‘π‘œπ‘˜π‘’π‘›π‘  is the result of lexically analysing a module and adding column-number indicators to it as described above. The definition of 𝐿 is as follows, where we use β€œ:” as a stream construction operator, and β€œ[]” for the empty stream.

L(<n>:ts)(m:ms)=; : (L ts (m : ms))if π‘š=𝑛
=} : (L(<n>:ts) ms)if 𝑛<π‘š
L(<n>:ts) ms=L ts ms
L({n} : ts)(m:ms)={ : (L ts (n : m : ms)if 𝑛>π‘š (Note 1)
L({n} : ts)[]={ : (L ts [n])if 𝑛>0 (Note 1)
L({n} : ts) ms={ : } : (L (<n> : ts) ms)(Note 2)
L(} : ts)(0:ms)=} : (L ts ms)(Note 3)
L(} : ts) ms=parse-error(Note 3)
L({ : ts) ms={ : (L ts (0 : ms))(Note 4)
L(t : ts)(m:ms)=} : (L (t:ts) ms)if m /= 0 and parse-error(𝑑) (Note 5)
L(t : ts) ms=t : (L ts ms)
L [] []=[]
L [] (m : ms)=} : (L [] ms)if π‘šβ‰ 0 (Note 6)
Note 1.

A nested context must be further indented than the enclosing context (𝑛>π‘š). If not, 𝐿 fails, and the compiler should indicate a layout error. An example is:

f x = let
         h y = let
  p z = z
               in p
      in h

Here, the definition of p is indented less than the indentation of the enclosing context, which is set in this case by the definition of h.

Note 2.

If the first token after a where (say) is not indented more than the enclosing layout context, then the block must be empty, so empty braces are inserted. The {𝑛} token is replaced by <𝑛>, to mimic the situation if the empty braces had been explicit.

Note 3.

By matching against 0 for the current layout context, we ensure that an explicit close brace can only match an explicit open brace. A parse error results if an explicit close brace matches an implicit open brace.

Note 4.

This clause means that all brace pairs are treated as explicit layout contexts, including labelled construction and update (SectionΒ 3.15). This is a difference between this formulation and Haskell 1.4.

Note 5.

The side condition π‘π‘Žπ‘Ÿπ‘ π‘’-π‘’π‘Ÿπ‘Ÿπ‘œπ‘Ÿ(𝑑) is to be interpreted as follows: if the tokens generated so far by 𝐿 together with the next token 𝑑 represent an invalid prefix of the Haskell grammar, and the tokens generated so far by 𝐿 followed by the token β€œ}” represent a valid prefix of the Haskell grammar, then π‘π‘Žπ‘Ÿπ‘ π‘’-π‘’π‘Ÿπ‘Ÿπ‘œπ‘Ÿ(𝑑) is true.

The test m /= 0 checks that an implicitly-added closing brace would match an implicit open brace.

Note 6.

At the end of the input, any pending close-braces are inserted. It is an error at this point to be within a non-layout context (i.e. π‘š=0).

If none of the rules given above matches, then the algorithm fails. It can fail for instance when the end of the input is reached, and a non-layout context is active, since the close brace is missing. Some error conditions are not detected by the algorithm, although they could be: for example let }.

Note 1 implements the feature that layout processing can be stopped prematurely by a parse error. For example

let x = e; y = x in e'

is valid, because it translates to

let { x = e; y = x } in e'

The close brace is inserted due to the parse error rule above.

10.4 Literate comments

The β€œliterate comment” convention, first developed by Richard Bird and Philip Wadler for Orwell, and inspired in turn by Donald Knuth’s β€œliterate programming”, is an alternative style for encoding Haskell source code. The literate style encourages comments by making them the default. A line in which β€œ>” is the first character is treated as part of the program; all other lines are comments.

The program text is recovered by taking only those lines beginning with β€œ>”, and replacing the leading β€œ>” with a space. Layout and comments apply exactly as described in ChapterΒ 10 in the resulting text.

To capture some cases where one omits an β€œ>” by mistake, it is an error for a program line to appear adjacent to a non-blank comment line, where a line is taken as blank if it consists only of whitespace.

By convention, the style of comment is indicated by the file extension, with β€œ.hs” indicating a usual Haskell file and β€œ.lhs” indicating a literate Haskell file. Using this style, a simple factorial program would be:

   This literate program prompts the user for a number
   and prints the factorial of that number:

> main :: IO ()

> main = do putStr "Enter a number: "
>           l <- readLine
>           putStr "n!= "
>           print (fact (read l))
          
  This is the factorial function.

> fact :: Integer -> Integer
> fact 0 = 1
> fact n = n * fact (n-1)

An alternative style of literate programming is particularly suitable for use with the LaTeX text processing system. In this convention, only those parts of the literate program that are entirely enclosed between \begin{code}...\end{code} delimiters are treated as program text; all other lines are comments. More precisely:

It is not necessary to insert additional blank lines before or after these delimiters, though it may be stylistically desirable. For example,

\documentstyle{article}

\begin{document}

\chapter{Introduction}

This is a trivial program that prints the first 20 factorials.

\begin{code}
main :: IO ()
main =  print [ (n, product [1..n]) | n <- [1..20]]
\end{code}

\end{document}

This style uses the same file extension. It is not advisable to mix these two styles in the same file.

10.5 Context-Free Syntax

π‘šπ‘œπ‘‘π‘’π‘™π‘’β†’πš–πš˜πšπšžπš•πšŽπ‘šπ‘œπ‘‘π‘–π‘‘[𝑒π‘₯π‘π‘œπ‘Ÿπ‘‘π‘ ]πš πš‘πšŽπš›πšŽπ‘π‘œπ‘‘π‘¦
|π‘π‘œπ‘‘π‘¦
π‘π‘œπ‘‘π‘¦β†’{π‘–π‘šπ‘π‘‘π‘’π‘π‘™π‘ ;π‘‘π‘œπ‘π‘‘π‘’π‘π‘™π‘ }
|{π‘–π‘šπ‘π‘‘π‘’π‘π‘™π‘ }
|{π‘‘π‘œπ‘π‘‘π‘’π‘π‘™π‘ }
π‘–π‘šπ‘π‘‘π‘’π‘π‘™π‘ β†’π‘–π‘šπ‘π‘‘π‘’π‘π‘™1;…;π‘–π‘šπ‘π‘‘π‘’π‘π‘™π‘›(𝑛β‰₯1)
𝑒π‘₯π‘π‘œπ‘Ÿπ‘‘π‘ β†’(𝑒π‘₯π‘π‘œπ‘Ÿπ‘‘1,…,𝑒π‘₯π‘π‘œπ‘Ÿπ‘‘π‘›[,])(𝑛β‰₯0)
𝑒π‘₯π‘π‘œπ‘Ÿπ‘‘β†’π‘žπ‘£π‘Žπ‘Ÿ
|π‘žπ‘‘π‘¦π‘π‘œπ‘›[(..)|(π‘π‘›π‘Žπ‘šπ‘’1,…,π‘π‘›π‘Žπ‘šπ‘’π‘›)](𝑛β‰₯0)
|π‘žπ‘‘π‘¦π‘π‘™π‘ [(..)|(π‘£π‘Žπ‘Ÿ1,…,π‘£π‘Žπ‘Ÿπ‘›)](𝑛β‰₯0)
|πš–πš˜πšπšžπš•πšŽπ‘šπ‘œπ‘‘π‘–π‘‘
π‘–π‘šπ‘π‘‘π‘’π‘π‘™β†’πš’πš–πš™πš˜πš›πš[πššπšžπšŠπš•πš’πšπš’πšŽπš]π‘šπ‘œπ‘‘π‘–π‘‘[πšŠπšœπ‘šπ‘œπ‘‘π‘–π‘‘][π‘–π‘šπ‘π‘ π‘π‘’π‘]
|(empty declaration)
π‘–π‘šπ‘π‘ π‘π‘’π‘β†’(π‘–π‘šπ‘π‘œπ‘Ÿπ‘‘1,…,π‘–π‘šπ‘π‘œπ‘Ÿπ‘‘π‘›[,])(𝑛β‰₯0)
|πš‘πš’πšπš’πš—πš(π‘–π‘šπ‘π‘œπ‘Ÿπ‘‘1,…,π‘–π‘šπ‘π‘œπ‘Ÿπ‘‘π‘›[,])(𝑛β‰₯0)
π‘–π‘šπ‘π‘œπ‘Ÿπ‘‘β†’π‘£π‘Žπ‘Ÿ
|π‘‘π‘¦π‘π‘œπ‘›[(..)|(π‘π‘›π‘Žπ‘šπ‘’1,…,π‘π‘›π‘Žπ‘šπ‘’π‘›)](𝑛β‰₯0)
|𝑑𝑦𝑐𝑙𝑠[(..)|(π‘£π‘Žπ‘Ÿ1,…,π‘£π‘Žπ‘Ÿπ‘›)](𝑛β‰₯0)
π‘π‘›π‘Žπ‘šπ‘’β†’π‘£π‘Žπ‘Ÿ|π‘π‘œπ‘›
π‘‘π‘œπ‘π‘‘π‘’π‘π‘™π‘ β†’π‘‘π‘œπ‘π‘‘π‘’π‘π‘™1;…;π‘‘π‘œπ‘π‘‘π‘’π‘π‘™π‘›(𝑛β‰₯1)
π‘‘π‘œπ‘π‘‘π‘’π‘π‘™β†’πšπš’πš™πšŽπ‘ π‘–π‘šπ‘π‘™π‘’π‘‘π‘¦π‘π‘’=𝑑𝑦𝑝𝑒
|𝚍𝚊𝚝𝚊[π‘π‘œπ‘›π‘‘π‘’π‘₯𝑑=>]π‘ π‘–π‘šπ‘π‘™π‘’π‘‘π‘¦π‘π‘’[=π‘π‘œπ‘›π‘ π‘‘π‘Ÿπ‘ ][π‘‘π‘’π‘Ÿπ‘–π‘£π‘–π‘›π‘”]
|πš—πšŽπš πšπš’πš™πšŽ[π‘π‘œπ‘›π‘‘π‘’π‘₯𝑑=>]π‘ π‘–π‘šπ‘π‘™π‘’π‘‘π‘¦π‘π‘’=π‘›π‘’π‘€π‘π‘œπ‘›π‘ π‘‘π‘Ÿ[π‘‘π‘’π‘Ÿπ‘–π‘£π‘–π‘›π‘”]
|πšŒπš•πšŠπšœπšœ[π‘ π‘π‘œπ‘›π‘‘π‘’π‘₯𝑑=>]π‘‘π‘¦π‘π‘™π‘ π‘‘π‘¦π‘£π‘Žπ‘Ÿ[πš πš‘πšŽπš›πšŽπ‘π‘‘π‘’π‘π‘™π‘ ]
|πš’πš—πšœπšπšŠπš—πšŒπšŽ[π‘ π‘π‘œπ‘›π‘‘π‘’π‘₯𝑑=>]π‘žπ‘‘π‘¦π‘π‘™π‘ π‘–π‘›π‘ π‘‘[πš πš‘πšŽπš›πšŽπ‘–π‘‘π‘’π‘π‘™π‘ ]
|πšπšŽπšπšŠπšžπš•πš(𝑑𝑦𝑝𝑒1,…,𝑑𝑦𝑝𝑒𝑛)(𝑛β‰₯0)
|πšπš˜πš›πšŽπš’πšπš—π‘“π‘‘π‘’π‘π‘™
|𝑑𝑒𝑐𝑙
𝑑𝑒𝑐𝑙𝑠→{𝑑𝑒𝑐𝑙1;…;𝑑𝑒𝑐𝑙𝑛}(𝑛β‰₯0)
𝑑𝑒𝑐𝑙→𝑔𝑒𝑛𝑑𝑒𝑐𝑙
|(π‘“π‘’π‘›π‘™β„Žπ‘ |π‘π‘Žπ‘‘)π‘Ÿβ„Žπ‘ 
𝑐𝑑𝑒𝑐𝑙𝑠→{𝑐𝑑𝑒𝑐𝑙1;…;𝑐𝑑𝑒𝑐𝑙𝑛}(𝑛β‰₯0)
𝑐𝑑𝑒𝑐𝑙→𝑔𝑒𝑛𝑑𝑒𝑐𝑙
|(π‘“π‘’π‘›π‘™β„Žπ‘ |π‘£π‘Žπ‘Ÿ)π‘Ÿβ„Žπ‘ 
𝑖𝑑𝑒𝑐𝑙𝑠→{𝑖𝑑𝑒𝑐𝑙1;…;𝑖𝑑𝑒𝑐𝑙𝑛}(𝑛β‰₯0)
𝑖𝑑𝑒𝑐𝑙→(π‘“π‘’π‘›π‘™β„Žπ‘ |π‘£π‘Žπ‘Ÿ)π‘Ÿβ„Žπ‘ 
|(empty)
π‘”π‘’π‘›π‘‘π‘’π‘π‘™β†’π‘£π‘Žπ‘Ÿπ‘ ::[π‘π‘œπ‘›π‘‘π‘’π‘₯𝑑=>]𝑑𝑦𝑝𝑒(type signature)
|𝑓𝑖π‘₯𝑖𝑑𝑦[π‘–π‘›π‘‘π‘’π‘”π‘’π‘Ÿ]π‘œπ‘π‘ (fixity declaration)
|(empty declaration)
π‘œπ‘π‘ β†’π‘œπ‘1,…,π‘œπ‘π‘›(𝑛β‰₯1)
π‘£π‘Žπ‘Ÿπ‘ β†’π‘£π‘Žπ‘Ÿ1,…,π‘£π‘Žπ‘Ÿπ‘›(𝑛β‰₯1)
𝑓𝑖π‘₯π‘–π‘‘π‘¦β†’πš’πš—πšπš’πš‘πš•|πš’πš—πšπš’πš‘πš›|πš’πš—πšπš’πš‘
𝑑𝑦𝑝𝑒→𝑏𝑑𝑦𝑝𝑒[->𝑑𝑦𝑝𝑒](function type)
𝑏𝑑𝑦𝑝𝑒→[𝑏𝑑𝑦𝑝𝑒]π‘Žπ‘‘π‘¦π‘π‘’(type application)
π‘Žπ‘‘π‘¦π‘π‘’β†’π‘”π‘‘π‘¦π‘π‘œπ‘›
|π‘‘π‘¦π‘£π‘Žπ‘Ÿ
|(𝑑𝑦𝑝𝑒1,…,π‘‘π‘¦π‘π‘’π‘˜)(tuple type, π‘˜β‰₯2)
|[𝑑𝑦𝑝𝑒](list type)
|(𝑑𝑦𝑝𝑒)(parenthesized constructor)
π‘”π‘‘π‘¦π‘π‘œπ‘›β†’π‘žπ‘‘π‘¦π‘π‘œπ‘›
|()(unit type)
|[](list constructor)
|(->)(function constructor)
|(,{,})(tupling constructors)
π‘π‘œπ‘›π‘‘π‘’π‘₯π‘‘β†’π‘π‘™π‘Žπ‘ π‘ 
|(π‘π‘™π‘Žπ‘ π‘ 1,…,π‘π‘™π‘Žπ‘ π‘ π‘›)(𝑛β‰₯0)
π‘π‘™π‘Žπ‘ π‘ β†’π‘žπ‘‘π‘¦π‘π‘™π‘ π‘‘π‘¦π‘£π‘Žπ‘Ÿ
|π‘žπ‘‘π‘¦π‘π‘™π‘ (π‘‘π‘¦π‘£π‘Žπ‘Ÿπ‘Žπ‘‘π‘¦π‘π‘’1β€¦π‘Žπ‘‘π‘¦π‘π‘’π‘›)(𝑛β‰₯1)
π‘ π‘π‘œπ‘›π‘‘π‘’π‘₯π‘‘β†’π‘ π‘–π‘šπ‘π‘™π‘’π‘π‘™π‘Žπ‘ π‘ 
|(π‘ π‘–π‘šπ‘π‘™π‘’π‘π‘™π‘Žπ‘ π‘ 1,…,π‘ π‘–π‘šπ‘π‘™π‘’π‘π‘™π‘Žπ‘ π‘ π‘›)(𝑛β‰₯0)
π‘ π‘–π‘šπ‘π‘™π‘’π‘π‘™π‘Žπ‘ π‘ β†’π‘žπ‘‘π‘¦π‘π‘™π‘ π‘‘π‘¦π‘£π‘Žπ‘Ÿ
π‘ π‘–π‘šπ‘π‘™π‘’π‘‘π‘¦π‘π‘’β†’π‘‘π‘¦π‘π‘œπ‘›π‘‘π‘¦π‘£π‘Žπ‘Ÿ1β€¦π‘‘π‘¦π‘£π‘Žπ‘Ÿπ‘˜(π‘˜β‰₯0)
π‘π‘œπ‘›π‘ π‘‘π‘Ÿπ‘ β†’π‘π‘œπ‘›π‘ π‘‘π‘Ÿ1|…|π‘π‘œπ‘›π‘ π‘‘π‘Ÿπ‘›(𝑛β‰₯1)
π‘π‘œπ‘›π‘ π‘‘π‘Ÿβ†’π‘π‘œπ‘›[!]π‘Žπ‘‘π‘¦π‘π‘’1…[!]π‘Žπ‘‘π‘¦π‘π‘’π‘˜(arity π‘π‘œπ‘›=π‘˜, π‘˜β‰₯0)
|(𝑏𝑑𝑦𝑝𝑒|!π‘Žπ‘‘π‘¦π‘π‘’)π‘π‘œπ‘›π‘œπ‘(𝑏𝑑𝑦𝑝𝑒|!π‘Žπ‘‘π‘¦π‘π‘’)(infix π‘π‘œπ‘›π‘œπ‘)
|π‘π‘œπ‘›{𝑓𝑖𝑒𝑙𝑑𝑑𝑒𝑐𝑙1,…,𝑓𝑖𝑒𝑙𝑑𝑑𝑒𝑐𝑙𝑛}(𝑛β‰₯0)
π‘›π‘’π‘€π‘π‘œπ‘›π‘ π‘‘π‘Ÿβ†’π‘π‘œπ‘›π‘Žπ‘‘π‘¦π‘π‘’
|π‘π‘œπ‘›{π‘£π‘Žπ‘Ÿ::𝑑𝑦𝑝𝑒}
π‘“π‘–π‘’π‘™π‘‘π‘‘π‘’π‘π‘™β†’π‘£π‘Žπ‘Ÿπ‘ ::(𝑑𝑦𝑝𝑒|!π‘Žπ‘‘π‘¦π‘π‘’)
π‘‘π‘’π‘Ÿπ‘–π‘£π‘–π‘›π‘”β†’πšπšŽπš›πš’πšŸπš’πš—πš(π‘‘π‘π‘™π‘Žπ‘ π‘ |(π‘‘π‘π‘™π‘Žπ‘ π‘ 1,…,π‘‘π‘π‘™π‘Žπ‘ π‘ π‘›))(𝑛β‰₯0)
π‘‘π‘π‘™π‘Žπ‘ π‘ β†’π‘žπ‘‘π‘¦π‘π‘™π‘ 
π‘–π‘›π‘ π‘‘β†’π‘”π‘‘π‘¦π‘π‘œπ‘›
|(π‘”π‘‘π‘¦π‘π‘œπ‘›π‘‘π‘¦π‘£π‘Žπ‘Ÿ1β€¦π‘‘π‘¦π‘£π‘Žπ‘Ÿπ‘˜)(π‘˜β‰₯0, π‘‘π‘¦π‘£π‘Žπ‘Ÿπ‘  distinct)
|(π‘‘π‘¦π‘£π‘Žπ‘Ÿ1,…,π‘‘π‘¦π‘£π‘Žπ‘Ÿπ‘˜)(π‘˜β‰₯2, π‘‘π‘¦π‘£π‘Žπ‘Ÿπ‘  distinct)
|[π‘‘π‘¦π‘£π‘Žπ‘Ÿ]
|(π‘‘π‘¦π‘£π‘Žπ‘Ÿ1->π‘‘π‘¦π‘£π‘Žπ‘Ÿ2)(π‘‘π‘¦π‘£π‘Žπ‘Ÿ1 and π‘‘π‘¦π‘£π‘Žπ‘Ÿ2 distinct)
π‘“π‘‘π‘’π‘π‘™β†’πš’πš–πš™πš˜πš›πšπ‘π‘Žπ‘™π‘™π‘π‘œπ‘›π‘£[π‘ π‘Žπ‘“π‘’π‘‘π‘¦]π‘–π‘šπ‘π‘’π‘›π‘‘π‘£π‘Žπ‘Ÿ::𝑓𝑑𝑦𝑝𝑒(define variable)
|πšŽπš‘πš™πš˜πš›πšπ‘π‘Žπ‘™π‘™π‘π‘œπ‘›π‘£π‘’π‘₯π‘π‘’π‘›π‘‘π‘£π‘Žπ‘Ÿ::𝑓𝑑𝑦𝑝𝑒(expose variable)
π‘π‘Žπ‘™π‘™π‘π‘œπ‘›π‘£β†’πšŒπšŒπšŠπš•πš•|πšœπšπšπšŒπšŠπš•πš•|πšŒπš™πš•πšžπšœπš™πš•πšžπšœ(calling convention)
|πš“πšŸπš–|πšπš˜πšπš—πšŽπš
|system-specific calling conventions
π‘–π‘šπ‘π‘’π‘›π‘‘β†’[π‘ π‘‘π‘Ÿπ‘–π‘›π‘”]See Section X
𝑒π‘₯𝑝𝑒𝑛𝑑→[π‘ π‘‘π‘Ÿπ‘–π‘›π‘”]See Section X
π‘ π‘Žπ‘“π‘’π‘‘π‘¦β†’πšžπš—πšœπšŠπšπšŽ|𝚜𝚊𝚏𝚎
π‘“π‘‘π‘¦π‘π‘’β†’π‘“π‘Ÿπ‘‘π‘¦π‘π‘’
|π‘“π‘Žπ‘‘π‘¦π‘π‘’->𝑓𝑑𝑦𝑝𝑒
π‘“π‘Ÿπ‘‘π‘¦π‘π‘’β†’π‘“π‘Žπ‘‘π‘¦π‘π‘’
|()
π‘“π‘Žπ‘‘π‘¦π‘π‘’β†’π‘žπ‘‘π‘¦π‘π‘œπ‘›π‘Žπ‘‘π‘¦π‘π‘’1β€¦π‘Žπ‘‘π‘¦π‘π‘’π‘˜(π‘˜β‰₯0)
π‘“π‘’π‘›π‘™β„Žπ‘ β†’π‘£π‘Žπ‘Ÿπ‘Žπ‘π‘Žπ‘‘{π‘Žπ‘π‘Žπ‘‘}
|π‘π‘Žπ‘‘π‘£π‘Žπ‘Ÿπ‘œπ‘π‘π‘Žπ‘‘
|(π‘“π‘’π‘›π‘™β„Žπ‘ )π‘Žπ‘π‘Žπ‘‘{π‘Žπ‘π‘Žπ‘‘}
π‘Ÿβ„Žπ‘ β†’=𝑒π‘₯𝑝[πš πš‘πšŽπš›πšŽπ‘‘π‘’π‘π‘™π‘ ]
|π‘”π‘‘π‘Ÿβ„Žπ‘ [πš πš‘πšŽπš›πšŽπ‘‘π‘’π‘π‘™π‘ ]
π‘”π‘‘π‘Ÿβ„Žπ‘ β†’π‘”π‘’π‘Žπ‘Ÿπ‘‘π‘ =𝑒π‘₯𝑝[π‘”π‘‘π‘Ÿβ„Žπ‘ ]
π‘”π‘’π‘Žπ‘Ÿπ‘‘π‘ β†’|π‘”π‘’π‘Žπ‘Ÿπ‘‘1,…,π‘”π‘’π‘Žπ‘Ÿπ‘‘π‘›(𝑛β‰₯1)
π‘”π‘’π‘Žπ‘Ÿπ‘‘β†’π‘π‘Žπ‘‘<-𝑖𝑛𝑓𝑖π‘₯𝑒π‘₯𝑝(pattern guard)
|πš•πšŽπšπ‘‘π‘’π‘π‘™π‘ (local declaration)
|𝑖𝑛𝑓𝑖π‘₯𝑒π‘₯𝑝
𝑒π‘₯𝑝→𝑖𝑛𝑓𝑖π‘₯𝑒π‘₯𝑝::[π‘π‘œπ‘›π‘‘π‘’π‘₯𝑑=>]𝑑𝑦𝑝𝑒(expression type signature)
|𝑖𝑛𝑓𝑖π‘₯𝑒π‘₯𝑝
𝑖𝑛𝑓𝑖π‘₯𝑒π‘₯𝑝→𝑙𝑒π‘₯π‘π‘žπ‘œπ‘π‘–π‘›π‘“π‘–π‘₯𝑒π‘₯𝑝
|-𝑖𝑛𝑓𝑖π‘₯𝑒π‘₯𝑝(prefix negation)
|𝑙𝑒π‘₯𝑝
𝑙𝑒π‘₯𝑝→\π‘Žπ‘π‘Žπ‘‘1β€¦π‘Žπ‘π‘Žπ‘‘π‘›->𝑒π‘₯𝑝(lambda abstraction, 𝑛β‰₯1)
|πš•πšŽπšπ‘‘π‘’π‘π‘™π‘ πš’πš—π‘’π‘₯𝑝(let expression)
|πš’πšπ‘’π‘₯𝑝[;]πšπš‘πšŽπš—π‘’π‘₯𝑝[;]πšŽπš•πšœπšŽπ‘’π‘₯𝑝(conditional)
|πšŒπšŠπšœπšŽπ‘’π‘₯π‘πš˜πš{π‘Žπ‘™π‘‘π‘ }(case expression)
|𝚍𝚘{π‘ π‘‘π‘šπ‘‘π‘ }(do expression)
|𝑓𝑒π‘₯𝑝
𝑓𝑒π‘₯𝑝→[𝑓𝑒π‘₯𝑝]π‘Žπ‘’π‘₯𝑝(function application)
π‘Žπ‘’π‘₯π‘β†’π‘žπ‘£π‘Žπ‘Ÿ(variable)
|π‘”π‘π‘œπ‘›(general constructor)
|π‘™π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘™
|(𝑒π‘₯𝑝)(parenthesized expression)
|(𝑒π‘₯𝑝1,…,𝑒π‘₯π‘π‘˜)(tuple, π‘˜β‰₯2)
|[𝑒π‘₯𝑝1,…,𝑒π‘₯π‘π‘˜](list, π‘˜β‰₯1)
|[𝑒π‘₯𝑝1[,𝑒π‘₯𝑝2]..[𝑒π‘₯𝑝3]](arithmetic sequence)
|[𝑒π‘₯𝑝|π‘žπ‘’π‘Žπ‘™1,…,π‘žπ‘’π‘Žπ‘™π‘›](list comprehension, 𝑛β‰₯1)
|(𝑖𝑛𝑓𝑖π‘₯𝑒π‘₯π‘π‘žπ‘œπ‘)(left section)
|(π‘žπ‘œπ‘βŸ¨-βŸ©π‘–π‘›π‘“π‘–π‘₯𝑒π‘₯𝑝)(right section)
|π‘žπ‘π‘œπ‘›{𝑓𝑏𝑖𝑛𝑑1,…,𝑓𝑏𝑖𝑛𝑑𝑛}(labeled construction, 𝑛β‰₯0)
|π‘Žπ‘’π‘₯π‘βŸ¨π‘žπ‘π‘œπ‘›βŸ©{𝑓𝑏𝑖𝑛𝑑1,…,𝑓𝑏𝑖𝑛𝑑𝑛}(labeled update, 𝑛β‰₯1)
π‘žπ‘’π‘Žπ‘™β†’π‘π‘Žπ‘‘<-𝑒π‘₯𝑝(generator)
|πš•πšŽπšπ‘‘π‘’π‘π‘™π‘ (local declaration)
|𝑒π‘₯𝑝(guard)
π‘Žπ‘™π‘‘π‘ β†’π‘Žπ‘™π‘‘1;…;π‘Žπ‘™π‘‘π‘›(𝑛β‰₯1)
π‘Žπ‘™π‘‘β†’π‘π‘Žπ‘‘->𝑒π‘₯𝑝[πš πš‘πšŽπš›πšŽπ‘‘π‘’π‘π‘™π‘ ]
|π‘π‘Žπ‘‘π‘”π‘‘π‘π‘Žπ‘‘[πš πš‘πšŽπš›πšŽπ‘‘π‘’π‘π‘™π‘ ]
|(empty alternative)
π‘”π‘‘π‘π‘Žπ‘‘β†’π‘”π‘’π‘Žπ‘Ÿπ‘‘π‘ ->𝑒π‘₯𝑝[π‘”π‘‘π‘π‘Žπ‘‘]
π‘ π‘‘π‘šπ‘‘π‘ β†’π‘ π‘‘π‘šπ‘‘1β€¦π‘ π‘‘π‘šπ‘‘π‘›π‘’π‘₯𝑝[;](𝑛β‰₯0)
π‘ π‘‘π‘šπ‘‘β†’π‘’π‘₯𝑝;
|π‘π‘Žπ‘‘<-𝑒π‘₯𝑝;
|πš•πšŽπšπ‘‘π‘’π‘π‘™π‘ ;
|;(empty statement)
π‘“π‘π‘–π‘›π‘‘β†’π‘žπ‘£π‘Žπ‘Ÿ=𝑒π‘₯𝑝
π‘π‘Žπ‘‘β†’π‘™π‘π‘Žπ‘‘π‘žπ‘π‘œπ‘›π‘œπ‘π‘π‘Žπ‘‘(infix constructor)
|π‘™π‘π‘Žπ‘‘
π‘™π‘π‘Žπ‘‘β†’π‘Žπ‘π‘Žπ‘‘
|-(π‘–π‘›π‘‘π‘’π‘”π‘’π‘Ÿ|π‘“π‘™π‘œπ‘Žπ‘‘)(negative literal)
|π‘”π‘π‘œπ‘›π‘Žπ‘π‘Žπ‘‘1β€¦π‘Žπ‘π‘Žπ‘‘π‘˜(arity π‘”π‘π‘œπ‘›=π‘˜, π‘˜β‰₯1)
π‘Žπ‘π‘Žπ‘‘β†’π‘£π‘Žπ‘Ÿ[@π‘Žπ‘π‘Žπ‘‘](as pattern)
|π‘”π‘π‘œπ‘›(arity π‘”π‘π‘œπ‘›=0)
|π‘žπ‘π‘œπ‘›{π‘“π‘π‘Žπ‘‘1,…,π‘“π‘π‘Žπ‘‘π‘˜}(labeled pattern, π‘˜β‰₯0)
|π‘™π‘–π‘‘π‘’π‘Ÿπ‘Žπ‘™
|_(wildcard)
|(π‘π‘Žπ‘‘)(parenthesized pattern)
|(π‘π‘Žπ‘‘1,…,π‘π‘Žπ‘‘)(tuple pattern, π‘˜β‰₯2)
|[π‘π‘Žπ‘‘1,…,π‘π‘Žπ‘‘](list pattern, π‘˜β‰₯1)
|~π‘Žπ‘π‘Žπ‘‘(irrefutable pattern)
π‘“π‘π‘Žπ‘‘β†’π‘žπ‘£π‘Žπ‘Ÿ=π‘π‘Žπ‘‘
π‘”π‘π‘œπ‘›β†’()
|[]
|(,{,})
|π‘žπ‘π‘œπ‘›
π‘£π‘Žπ‘Ÿβ†’π‘£π‘Žπ‘Ÿπ‘–π‘‘|(π‘£π‘Žπ‘Ÿπ‘ π‘¦π‘š)(variable)
π‘žπ‘£π‘Žπ‘Ÿβ†’π‘žπ‘£π‘Žπ‘Ÿπ‘–π‘‘|(π‘žπ‘£π‘Žπ‘Ÿπ‘ π‘¦π‘š)(qualified variable)
π‘π‘œπ‘›β†’π‘π‘œπ‘›π‘–π‘‘|(π‘π‘œπ‘›π‘ π‘¦π‘š)(constructor)
π‘žπ‘π‘œπ‘›β†’π‘žπ‘π‘œπ‘›π‘–π‘‘|(π‘žπ‘π‘œπ‘›π‘ π‘¦π‘š)(qualified constructor)
π‘£π‘Žπ‘Ÿπ‘œπ‘β†’π‘£π‘Žπ‘Ÿπ‘ π‘¦π‘š|`π‘£π‘Žπ‘Ÿπ‘–π‘‘`(variable operator)
π‘žπ‘£π‘Žπ‘Ÿπ‘œπ‘β†’π‘žπ‘£π‘Žπ‘Ÿπ‘ π‘¦π‘š|`π‘žπ‘£π‘Žπ‘Ÿπ‘–π‘‘`(qualified variable operator)
π‘π‘œπ‘›π‘œπ‘β†’π‘π‘œπ‘›π‘ π‘¦π‘š|`π‘π‘œπ‘›π‘–π‘‘`(constructor operator)
π‘žπ‘π‘œπ‘›π‘œπ‘β†’π‘”π‘π‘œπ‘›π‘ π‘¦π‘š|`π‘žπ‘π‘œπ‘›π‘–π‘‘`(qualified constructor operator)
π‘œπ‘β†’π‘£π‘Žπ‘Ÿπ‘œπ‘|π‘π‘œπ‘›π‘œπ‘(operator)
π‘žπ‘œπ‘β†’π‘žπ‘£π‘Žπ‘Ÿπ‘œπ‘|π‘žπ‘π‘œπ‘›π‘œπ‘(qualified operator)
π‘”π‘π‘œπ‘›π‘ π‘¦π‘šβ†’:|π‘žπ‘π‘œπ‘›π‘ π‘¦π‘š

10.6 Fixity Resolution

The following is an example implementation of fixity resolution for Haskell expressions. Fixity resolution also applies to Haskell patterns, but patterns are a subset of expressions so in what follows we consider only expressions for simplicity.

The function resolve takes a list in which the elements are expressions or operators, i.e. an instance of the 𝑖𝑛𝑓𝑖π‘₯𝑒π‘₯𝑝 non-terminal in the context-free grammar. It returns either Just e where e is the resolved expression, or Nothing if the input does not represent a valid expression. In a compiler, of course, it would be better to return more information about the operators involved for the purposes of producing a useful error message, but the Maybe type will suffice to illustrate the algorithm here.

import Control.Monad

type Prec   = Int
type Var    = String

data Op = Op String Prec Fixity
  deriving (Eq,Show)

data Fixity = Leftfix | Rightfix | Nonfix
  deriving (Eq,Show)

data Exp = Var Var | OpApp Exp Op Exp | Neg Exp 
  deriving (Eq,Show)

data Tok = TExp Exp | TOp Op | TNeg
  deriving (Eq,Show)
   
resolve :: [Tok] -> Maybe Exp
resolve tokens = fmap fst $ parseNeg (Op "" (-1) Nonfix) tokens
  where
    parseNeg :: Op -> [Tok] -> Maybe (Exp,[Tok])
    parseNeg op1 (TExp e1 : rest)
       = parse op1 e1 rest
    parseNeg op1 (TNeg : rest)
       = do guard (prec1 < 6)
            (r, rest') <- parseNeg (Op "-" 6 Leftfix) rest
            parse op1 (Neg r) rest'
       where
          Op _ prec1 fix1 = op1

    parse :: Op -> Exp -> [Tok] -> Maybe (Exp, [Tok])
    parse _   e1 [] = Just (e1, [])
    parse op1 e1 (TOp op2 : rest) 
       -- case (1): check for illegal expressions
       | prec1 == prec2 && (fix1 /= fix2 || fix1 == Nonfix)
       = Nothing

       -- case (2): op1 and op2 should associate to the left
       | prec1 > prec2 || (prec1 == prec2 && fix1 == Leftfix)
       = Just (e1, TOp op2 : rest)

       -- case (3): op1 and op2 should associate to the right
       | otherwise
       = do (r,rest') <- parseNeg op2 rest
            parse op1 (OpApp e1 op2 r) rest'
       where
         Op _ prec1 fix1 = op1
         Op _ prec2 fix2 = op2

The algorithm works as follows. At each stage we have a call

      parse op1 E1 (op2 : tokens)

which means that we are looking at an expression like

      E0 `op1` E1 `op2` ...     (1)

(the caller holds E0). The job of parse is to build the expression to the right of op1, returning the expression and any remaining input.

There are three cases to consider:

  1. if op1 and op2 have the same precedence, but they do not have the same associativity, or they are declared to be nonfix, then the expression is illegal.

  2. If op1 has a higher precedence than op2, or op1 and op2 should left-associate, then we know that the expression to the right of op1 is E1, so we return this to the caller.

  3. Otherwise, we know we want to build an expression of the form E1 `op2` R. To find R, we call parseNeg op2 tokens to compute the expression to the right of op2, namely R (more about parseNeg below, but essentially if tokens is of the form (E2 : rest), then this is equivalent to parse op2 E2 rest). Now, we have

    E0 `op1` (E1 `op2` R) `op3` ...

    where op3 is the next operator in the input. This is an instance of (1) above, so to continue we call parse, with the new E1 == (E1 `op2` R).

To initialise the algorithm, we set op1 to be an imaginary operator with precedence lower than anything else. Hence parse will consume the whole input, and return the resulting expression.

The handling of the prefix negation operator, -, complicates matters only slightly. Recall that prefix negation has the same fixity as infix negation: left-associative with precedence 6. The operator to the left of -, if there is one, must have precedence lower than 6 for the expression to be legal. The negation operator itself may left-associate with operators of the same fixity (e.g. +). So for example -a + b is legal and resolves as (-a) + b, but a + -b is illegal.

The function parseNeg handles prefix negation. If we encounter a negation operator, and it is legal in this position (the operator to the left has precedence lower than 6), then we proceed in a similar way to case (3) above: compute the argument to - by recursively calling parseNeg, and then continue by calling parse.

Note that this algorithm is insensitive to the range and resolution of precedences. There is no reason in principle that Haskell should be limited to integral precedences in the range 1 to 10; a larger range, or fractional values, would present no additional difficulties.