I find that two spaces is a nice looking indentation for a subelements , but you have a lot of freedom in formatting for readability ( just don't outdent within the same level ).
Operator and function precedence
Operators in Haskell fall into multiple levels of precedence. Most of these are the same as you would expect from other programming languages. Multiplication takes precedence over addition , and so on ( so "2+3+4" is 10 , not 14 ). Haskell's standard documentation can provide the details.
There is, however, one "gotcha" in Haskell precedence where it is easy to make a mistake. Functions take precedence over operators. The result of this is that the expression "f g 5" means "apply g (and 5) as arguments to f" not "apply the result of (g 5) to f". Most of the time, this sort of error will produce a compiler error message, since, for example, f will require an Int as an argument rather than another function. However, sometimes the situation can be worse than this, and you can write something valid but wrong:
As with other languages, parentheses are extremely useful in disambiguating expressions where you have some doubt about procedure (or just want to document the intention explicitly). Notice, by the way, that parentheses are not used around function arguments in Haskell; but there is no harm in pretending they are, which just creates an extra expression grouping (as in res2 above).
Scope of names
Readers might think there is a conflict between two points in this tutorial. On the other hand, we have said that names are defined as expressions only once in a program; on the other hand, many of the examples use the same variable names repeatedly. Both points are true, but need to be refined.
Every name is defined exactly once within a given scope. Every function definition defines its own scope, and some constructs within definitions define their own narrower scopes. Fortunately, the "offside rule" that defines subelements also precisely defines variable scoping. A variable (a name, really) can only occur once with a given indentation block. Let's see an example, much like previous ones:
Needless to say, the example is unnecessarily confusing. It is worth understanding, however, especially since arguments only have a scope within particular function definition (and the same names can be used in other function definitions).
Breaking down the problem
One thing you will have noticed is that function definitions in Haskell tend to be extremely short compared to those in other languages. This is partly due to the concise syntax of Haskell, but a greater reason is because of the emphasis in functional programming of breaking down problems into their component parts (rather than just sort of "doing what needs to be done" at each point in an imperative program). This encourages reusability of parts, and allows much better verification than each part really does what it is supposed to do.
The small parts of function definitions may be broken out in several ways. One way is to define a multitude of useful support functions within a source file. and use them as needed. The examples in this tutorial have mostly done this. However, there are also two (equivalent) ways of defining support functions within the narrow scope of a single function definition: the let clause and the where clause. A simple example follows.
The three definitions are equivalent, but f2 and f3 chose to define a (trivial) support function sq within the definition scope.
Importing/exporting
Haskell also supports a module system that allows for larger scale modularity of functions (and also for types, which we have not covered in this introductory tutorial). The two basic elements of module control are specification of imports and specification of exports. The former is done with the import declaration; the latter with the module declaration. Some examples include:
You can see that Haskell provides considerable, and fine-grained control of where function definitions are visible to other functions. This module system helps build.