
				CONVENTIONS
				-----------

  Conventions for Erlang functions in packages and modern Erlang
  programming in general:

	Unused variables

		Always have the compiler option 'warn_unused_vars'
		turned on. Warnings are not generated for variables
		whose names begin with '_'.

		Eliminate all warnings from old code by renaming
		all variables that really are unused, e.g.:
		'X' -> '_X'. Doing this usually pays off, as it
		tracks down bugs and dead code.

	Return values in general

		- A function should *not* return wrapped values like
		  {ok,Value}/{error,Reason} to indicate success or
		  failure. The assumed behaviour should be success,
		  and failures should be signalled by exceptions,
		  as described below.

		- A function which is evaluated for its side effects
		  only should always return the atom 'ok'; this is
		  the typical Erlang "void" value.

	Use {value, X}/none:

		When a function may either return a value or signal
		that no value is available, always return {value, X}
		if there is a value, and 'none' otherwise.

		The exception is when there is an *obviously* better
		encoding, like e.g. "nonempty string/empty string"
		(when it is *definite* that "empty string" cannot be
		a legal value).

		Use the same convention for record fields which may or
		may not have a specified value. This makes it easy to
		write e.g.:

			#thingy{foo = get_foo_value(...)}

		where 'get_foo_value' returns  'none' or {value, ..},
		and later switch on the contents of that field:

			frob(#thingy{foo = none}) -> ...;
			frob(#thingy{foo = {value, X}}) -> ....

		Note that error handling is a separate issue - thus
		*do not* add a third case {error, ...}; see below for
		details on error handling and exceptions.

	Use is_... tests in guards

		Use the new 'is_...' versions of guard tests,
		rather than the old names (cf. above). E.g., not:

			X when float(X) -> ...

		but instead

			X when is_float(X) -> ...

		(The 'erl_tidy' tool in the syntax_tools distribution
		can be used to check this, or even rewrite the code
		automatically.)

		The reason is that guards should not have a separate
		name space from the rest of the program. Using the
		new names can make it possible to eventually
		generalise the kind of expressions allowed in guards.

	Guard tests and failure

		- Never write clause guards that rely on failures
		  for choosing the correct clause. The following
		  kind of programming is *strongly discouraged*:

			if hd(X) >= 0 ->
			     ...
			   true ->
			     % If X is a cons cell whose head is less
			     % than 0 *or* if X is not a cons cell:
			     ...
			end

		  since it assumes that if X is not a cons cell, then
		  the failure of hd(X) will simply cause the next
		  clause to be selected.

		  Never rely on this behaviour - it makes the program
		  difficult to understand, and it could possibly be
		  changed in a future version of Erlang, so that the
		  failure simply causes a normal exception instead.

	Use boolean-valued functions with good names!

		- A function that can be made into a simple yes/no test
		(a predicate) should always be written in that way.

		For example, do not define:

			sex(Thing) -> male | female

		but instead

			is_female(Thing) -> true | false

		(unless you know that other alternatives may be added
		some day).

		Do not use names like:

			female(Thing) -> ...

		since it does not make it clear whether 'female' is a
		predicate or a conversion operation.

		Do not use negative-form names, as in:

			not_male(Thing) -> true | false

		since it makes it difficult to remember if the name
		of a particular predicate was "is..." or "not_...".
		Also, double negatives become confusing, as in:

			if not not_male(Thing) -> ...

	Exceptions and errors

		- Always use 'try ... catch ... end' to handle
		  exceptions (when it finally becomes available).
		  The 'catch ...' expression is then obsolete
		  and should not be used in new code.

		- Unexpected errors (type errors, etc.) should cause
		  an exception of type 'error'.

		  This is raised by erl.lang:error(Reason) or
		  erlang:fault(Reason). (This is the same kind of
		  exception that is raised by errors in built-in
		  functions and operators.)

		   * Reason should be something like {badarg, X},
		     that is likely to be inspected by a human,
		     but might possibly be analyzed by a program.

		   * Errors should typically not be caught, but be
		     allowed to crash the process.

		   * Errors that are not caught cause the process to
		     terminate, and propagate the Reason as a
		     signal to all linked processes.

		- "Expected" possible errors in a function, such as
		  "file not found", "key not present in dictionary",
		  and so on, should be reported by an exception of
		  type 'throw'.

		  This is raised by erl.lang:throw(Value) or
		  erlang:throw(Value). (This is also used for
		  nonlocal returns in general.)

		   * Value should be something like 'file_not_found'
		     or {parse_error, Line}, that is intended to be
		     caught by the program at some point and analysed.

		   * Throws that are not caught by the process are
		     turned into a corresponding error exception with
		     Reason = {nocatch, Value}.

		- Explicit termination of the process is done by
		  raising an exception of type 'exit'.

		  This is raised by erl.lang:exit(Reason) or
		  erlang:exit(Reason). (If Reason is the atom 'normal',
		  and the exception is not caught, the behaviour is
		  indistinguishable from that when the evaluation of
		  the process' initial function call has finished.)

		   * Exits (unless caught) cause the process to
		     terminate, and propagate the Reason as a signal
		     to all linked processes.

		   * Reason should be as for a throw. (Process exits
		     can be compared to nonlocal function returns.)

		   * Exits should typically not be caught, but be
		     allowed to terminate the process.

		   * Exits should not be used for other purposes; these
		     are covered by 'throw' and 'error'.

	Obsolete/deprecated language features

		- Don't use 'catch ...' (see Exceptions above)
		  if possible - use 'try' instead (when it becomes
		  available).

		- Don't use tuples {Module, Function} as proper
		  functional values, as in:

			F = {lists, reverse}
			F([1,2,3]).

		  This "feature" is nothing but trouble (for one thing,
		  it hides important module dependencies from programs
		  like 'xref'), and it should preferably be removed from
		  future versions of Erlang.

		  Either use real funs:

			F = fun (Xs) -> lists:reverse(Xs) end,
			F([1,2,3])

		  or match out the names manually if you have to
		  use a dynamic callback hook:

			my_code({Module, Function}) ->
				Module:Function([1,2,3])

		- Don't use apply(Module, Function, Arguments)
		  unless the Arguments list actually *can* vary
		  arbitrarily in length. (This is rarely the case.)
		  Instead, use:

			Module:Function(Arg1, ... ArgN)

		  Where Module and/or Function is a variable.
		  (This is *much* more efficient than 'apply'.)

		  If Arguments can have one of a limited number of
		  lengths, then write an explicit switch:

			case Arguments of
			    []       -> Module:Function();
			    [A1]     -> Module:Function(A1);
			    [A1, A2] -> Module:Function(A1,A2)
			end

		  (This is still more efficient, and also has the
		  advantage that it catches illegal-length argument
		  lists.)

		  Never use erlang:apply/2. (If you did not know it
		  existed, then just forget about it again.)

		- Use spawn(Fun) and spawn_link(Fun) instead
		  of spawn/spawn_link(Module, Function, Arguments).
		  This is cleaner (cf. apply/3 above) and lets
		  you avoid exporting functions from a module
		  when they are merely starting points for a process.
		  E.g.:

			-module(my_server).
			-export([start/1]) ->
			start(Data) ->
			    spawn(fun () -> init(Data) end).
			init(Data) ->
			    State = setup_state(Data),
			    loop(State).
			loop(State) ->
			    ...

		  If the start function is in another module,
		  you should still use spawn/1, as in:

			spawn(fun () -> my_server:init(Data) end)

		  (Note that as soon as the tail call inside the fun
		  is executed, the fun itself becomes garbage.)

	Exported functions

		- Separate the user interface from other exported
		  functions that are only for internal use, or for
		  debugging, etc. The main module should only
		  export functions that are officially exported
		  and documented.

		  E.g., if a function needs to be exported for the
		  only reason that it is a checkpoint for dynamic
		  code updates, as is usually the case with the
		  main loop function in a server, then always place
		  this function in some other module than the one
		  which exports the user interface.

	Passing module names in parameters

		- If module names are passed around as data,
		  then *do not* hard-code such names. At the
		  very least, use a macro definition, as in:

			-define(CALLBACK_MODULE, my_server_callbacks).
			...
			start(...) ->
			    gen_server:start(?CALLBACK_MODULE, ...)

	Indexing

		 - Indexing should preferably start at 0. See for
		   example the module 'erl.lang.list'.
