Expand/Shrink

Constants

These are variables that are assigned an initial value that can never change e.g.
    constant MAX = 100
    constant Upper = MAX - 10, Lower = 5
    constant name_list = {"Fred", "George", "Larry"}
The result of any expression can be assigned to a constant, even one involving function calls, but once the assignment is made, the value of the constant variable is "locked in".

Constant declaration also supports multiple assignment syntax (this may make more sense after reading that section) e.g.
    constant {x, y, z} = {{},5,1.5}
--  constant x = {}, y = 5, z = 1.5 -- equivalent
Typically such desequencing makes much more sense when the rhs is dynamically generated, for instance a function call.

Phix also allows nested constant declarations via the ':=' operator, within a (nested) rhs1 sequence constant declaration, so instead of
    constant WSAEINTR  = 10004,
             WSAEACCES = 10013
    constant {ERROR_NO, ERROR_NAME, ERROR_SHORT} = columnize(
            {{WSAEINTR,  "WSAEINTR",  "Interrupted function call."},
             {WSAEACCES, "WSAEACCES", "Permission denied."}}
which gets more and more error prone and tedious as the table grows in size, you can define them like this:
    constant {ERROR_NO, ERROR_NAME, ERROR_SHORT} = columnize(
            {{WSAEINTR  := 10004, "WSAEINTR",  "Interrupted function call."},
             {WSAEACCES := 10013, "WSAEACCES", "Permission denied."}})
ie on the second line before the first ',' we see both a definition of WSAEINTR and a reference to it (the spaces around := are optional).
If with nested_globals is in force, WSAEINTR and WSAEACCES (ie anything declared via the ':=' operator) would be global, even when ERROR_XX are local. There is also a direct converse, in the form of "with nested_locals".

You can also, with some caution, use ":=$" syntax to declare basic-enum-style nested constants, for example
    constant ABCD = {A:=$, B:=$, C:=$, D:=$}
--  constant ABCD = {A:=1, B:=2, C:=3, D:=4} -- equivalent
Note however that pmain.e/ncdollar is reset to 1 by the `constant` keyword and incremented on use in this fashion only, and cannot be overidden mid-declaration, so if you tried for instance "1,$,3,$" (iyswim) you would get "1,1,3,2" rather than "1,2,3,4". Also note $ has a completely different meaning within an enum (previous item), and within subscripts (such that s[$] means s[-1] aka s[length(s)]), plus of course this must all be supported by both the normal desktop/Phix and by pwa/p2js, hence: the implementation of ":=$" within constant definitions is deliberately kept as trivial as possible, and constructs such as s[NXT:=$] are not supported and may give unpredictable results (usu -1), in which case you will need to resort to a preceding enum or explicit values (such as s[NXT:=7]), and likewise you cannot use A:=100+$ or A:=$+100, since that’s not (a plain and simple) ":=$". On the plus side it should be perfectly possible to use ":=$" in as deeply nested sequences as you like.

It may help to know that constants are really just variables which must be assigned on declaration, and for which subsequent assignment or other modification is prohibited. The actual constant values, both named and unnamed, are "pooled" so that only a single copy ever gets written out to a compiled executable file.

Several builtin constants are automatically defined in psym.e/syminit(), such as C_CHAR, C_BYTE, C_PTR, NULL, WINDOWS, LINUX, PI, LOCK_SHARED, LOCK_EXCLUSIVE, BLACK, GREEN, BLUE, DB_OK, DB_OPEN_FAIL, DB_EXISTS_ALREADY, MB_OK, MB_OKCANCEL, IDOK, IDCANCEL, IDYES, IDNO, SEEK_OK, and many more besides.

Constants can also be declared as global (application-wide), local (file-level), or private (routine-specific), see scope.

WARNING: except for literal integer constants, a pure forward call may result in constants being unassigned.
This should not be an issue if you have already invoked, for instance, a top-level main() after all points of call/declaration.

Several files in the builtins/ directory use local variables instead of constants, and hoist their assignment to an xinit/initx()
construct, with xinit set false at load-time, and initx() setting it true and assigning any non-integers, for example:
integer xinit = false
string x

procedure initx()
    xinit = true
    x = "x" -- (usually something more involved)
end procedure

global procedure p() -- (possibly forward called)
    if not xinit then initx() end if -- (* many)
    ...
end procedure
While the above might lose some benefits of "constant", it keeps the "once" aspect and is a completely safe way to deal with forward calls.

Types are optional on constant declarations, whether global, file-level, or local.
While a type check can occur on a constant declaration, it is more likely there just for clarification.

**DEV** everything below is currently being reconsidered.
Update:
As of version 1.0.2 local constants are limited to literal values only. A statement/declaration inside a routine such as `constant x = split("one two three")` would not only suffer the same unassigned issues just described on forward calls, and raise some questions over precisely when/where that split() call should occur, but also try to pull in the autoinclude for split(), effectively when part-way through defining another routine, and that way trigger some very strange errors [it probably would be reasonably straightforward to stop it autoincluding things at the wrong time]. However, the recommended solution to those kind of issues would or might be to call split() inside an `if not init then` construct as just explained, which means the result isn’t being stored in a constant anyway... the whole concept of constants is much more useful at a higher/more visible level and in reality local constants gain very little over local variables... plus it seems JavaScript recomputes all local constants are every time, unless of course like above, you explicitly manage all that side of things..., so, literals-only it is.
Aside:
Version 0.8.2 introduced an ill-conceived and pooly implemented static routine-level declaration, which only ever worked for integers and bools assigned to a literal or pre-existing file-level constant (not a parameter), and proved incompatible with p2js, hence the whole sad and sorry mess was removed in the 1.0.1 release. Given the sheer number of problems found when re-examined, I very much doubt that "static" saw any significant active service at all, and in fact, over 12 months later, not one single comment was ever made about "static" going awol.


Constant declarations must be grouped together at the start of any routine.
The reason for this is that the [implicit] return decrefs a range of the symtab, so either
the run-time must start checking every symtab entry (slowing everything down) or
DoRoutineDef() has to perform a potentially messy re-numbering operation (slowing everything down).
The compiler already defers parameter creation, for similar prior concerns, so probably best not to make it even more convoluted, eh?

Logically I suppose there is an argument that you ought be able to do something like this:
constant c1 = "a"
procedure p1()
    constant c1 = "p1a"
end procedure
However the compiler issues duplicate errors over c1.
After some consideration I decided to leave it like that, mainly because the debugger does not posess the same concept of scope as the compiler proper, and hence would probably display the "wrong" c1 if/when asked (by whatever definition of wrong that might be).
You can however do this:
procedure p1()
    constant c = "p1a"
    ?c
end procedure

procedure p2()
    constant c = "p2a"
    ?c
end procedure

p1()
p2()
Since the c drop out of scope at the end procedure (ditto function/type) there is no name clash.

See Also: multiple assignment, columnize
Expand/Shrink