Procedures

These perform some computation and may have a list of parameters, e.g.
    procedure plot(integer x, integer y, integer char='*')
        position(x, y)
        puts(1, char)
    end procedure
A copy of the value of each argument is passed in. The formal parameter variables may be modified inside the procedure but this does not affect the value of the arguments (in the calling routine).
Performance Note:

The interpreter does not actually copy sequences or floating-point numbers unless it becomes necessary. For example,
     y = {1,2,3,4,5,6,7,8.5,"ABC"}
     x = y
The statement x = y does not actually cause a new copy of y to be created. Both x and y will simply "point" to the same sequence. If we later perform x[3] = 9, then a separate sequence will be created for x in memory (although there will still be just one shared copy of 8.5 and "ABC") and y is not altered. The same thing applies to "copies" of arguments passed in to subroutines.

If there are no parameters, the parentheses are still required, on both the procedure declaration and any calls to it, e.g.
    procedure empty()
    end procedure
    ...
    empty()
Repeated consecutive parameter types may be omitted, for example in (integer a, b, string c, d) the compiler assumes b is another integer, and d is another string.

Optional parameters can be declared simply by defining a default value, as shown above for char (= and := are treated identically in routine declarations). In many languages variable-length parameter lists are impossible. In C, you must set up strange mechanisms that are complex enough that the average programmer cannot do it without consulting a manual or a local guru. In phix the only rule is that all non-optional parameters must be grouped together on the left, and obviously any defaults must be legal values for the specified type.

Builtins with optional parameters include allocate, find, match, join, split, trim, round, and printf.

Alternatively, optional parameters can be emulated by specifying a single sequence parameter, which can be populated by anything from a zero-length sequence up to the maximum that the routine can handle. This approach is used, amongst others, by printf, call_proc, call_func, define_c_proc, define_c_func, c_proc, c_func, free, min, max, smallest, decode_flags, and join_path, as well as all the explicit sequence operators.

Named parameters can be used in any order desired using <name>:=<value> syntax. Quite possibly the ultimate example of named parameters is the timedelta routine:
    atom secs
    secs = timedelta(0,0,7,30,0,0,0)
    secs = timedelta(hours:=7, minutes:=30)
Both calls achieve exactly the same thing, but obviously there is a world of difference in code clarity.

You can supply the first few non-optional parameters as normal, and then start using named parameters, or you can use the names of all of them. Obviously you must supply all the non-optional parameters, somewhere, but once a name has been specified then all subsequent parameters must also be named.

Builtins for which I explicitly recommend the use of named parameters (especially for any uncommon or unusual uses) include clear_directory, copy_file, move_file, remove_directory, rename_file, date, join_path, prime_factors, split, split_any, and split_path.

Another example can be found in Edix:
    function saveFile(bool warnBackup=true)
Normal calls, which display a warning if for any reason the backup could not be made, are invoked as saveFile(), whereas saveFile(warnBackup:=false) is used when performing saveas or similar, in which case it still tries to make a backup but does not display any warning should it fail. The named parameter makes the slightly unusual invocation much clearer and self-documenting.

Alternatively, a local variable can be declared (eg bool warnBackup = false) and used as the (positional based) parameter, which will obviously work just as well, both in terms of program functionality and the improved code clarity. Although it requires no specific compiler support, and makes no check that such a parameter actually exists, I might sometimes also describe that mechanism as named parameters.

NB: On call statements, ":=" signifies the use of named parameters, whereas "=" has an entirely different meaning.
It is perfectly legitimate, assuming you have local variables x and y, to write plot(x:=x,y:=y), which would be equivalent to the simpler plot(x,y), whereas in the same circumstances the statement plot(x=x,y=y) would, barring unassigned errors, always be equivalent to plot(true,gtrue), aka plot(1,1).

Although somewhat obscure, it may be worth understanding the derivation of named parameter syntax: from proc(proc:name=value) via proc(:name=value) to the recommended proc(name:=value), with all three forms being perfectly legal code, what looks like a normal explict assignment operator is in fact a special "assign to the parameter namespace of the routine being called" operator.

Un-named parameters are also permitted (added in version 0.6.8). In many cases, especially callbacks, a routine must accept a fixed set of parameters, even if it does not use them. This can cause annoying "not used" warnings. Instead of
    function mainHandler(integer id, integer msg, atom wParam, object lParam)
        if id or wParam or object(lParam) then end if   -- suppress warnings
(in which the "if then if" generates no code) you can instead declare it as
    function mainHandler(integer /*id*/, integer msg, atom /*wParam*/, object /*lParam*/)
        -- (and obviously all comments, including this one, are optional)
Note that typechecking of un-named parameters still occurs, which can lead to interesting run-time error messages, eg
    procedure test(integer a, integer /*b*/, integer c)
        ?{a,c}
    end procedure
--  test(1,"error",2)   -- (causes a perfectly sensible compile-time error)
    call_proc(routine_id("test"),{1,"error",3})
triggers something like
C:\test\test.exw:1 in procedure test()
type check failure, ???(symtab[472][S_name]=0) is "error"
    a = 1
    c = 3
... called from test.exw:5

--> see C:\Program Files (x86)\Phix\ex.err
Press Enter...
- at least it indicates the offending source code line, and it should be fairly obvious the problem is with "/*b*/", for want of a better name. Of course no such errors would happen for parameters declared as type object. You should also be aware that the absence of "/*b*/" in the ex.err file, as deliberately shown above, may sometimes make debugging somewhat more difficult. One example of the latter that immediately springs to mind is pEmit2.e/blurph(), where I added a lineno parameter purely as a debugging aid. Admittedly I now fiddle with it to make it an even more effective debugging aid, but the implication still stands that making it unnamed would have rendered it ("/*lineno*/") utterly useless.