Non-Local Operator Assignment Is Not Guaranteed To Be Atomic Element

Overview¶

This documentation covers all the features of the Coconut Programming Language, and is intended as a reference/specification, not a tutorialized introduction. For a full introduction and tutorial of Coconut, see the tutorial.

Coconut is a variant of Python built for simple, elegant, Pythonic functional programming. Coconut syntax is a strict superset of Python 3 syntax. Thus, users familiar with Python will already be familiar with most of Coconut.

The Coconut compiler turns Coconut code into Python code. The primary method of accessing the Coconut compiler is through the Coconut command-line utility, which also features an interpreter for real-time compilation. In addition to the command-line utility, Coconut also supports the use of IPython/Jupyter notebooks.

While most of Coconut gets its inspiration simply from trying to make functional programming work in Python, additional inspiration came from Haskell, CoffeeScript, F#, and patterns.py.

Compilation¶

Installation¶

Using Pip¶

Since Coconut is hosted on the Python Package Index, it can be installed easily using . Simply install Python, open up a command-line prompt, and enter

which will install Coconut and its required dependencies.

Note: If you have an old version of Coconut installed and you want to upgrade, run instead.

If you are encountering errors running , try re-running it with the option. If works, but you cannot access the command, be sure that Coconut's installation location is in your environment variable. On UNIX, that is (without ) or (with ).

Using Conda¶

If you prefer to use instead of to manage your Python packages, you can also install Coconut using . Just install , open up a command-line prompt, and enter

which will properly create and build a recipe out of Coconut's feedstock.

Note: To use to install instead, just replace with in the last three commands above.

condaconfig--addchannelsconda-forgecondainstallcoconut

Optional Dependencies¶

Coconut also has optional dependencies, which can be installed by entering

or, to install multiple optional dependencies,

The full list of optional dependencies is:

  • : alias for (this is the recommended way to install a feature-complete version of Coconut),
  • : enables use of the / flag,
  • : enables use of the flag,
  • : improves use of the flag,
  • : enables use of the flag,
  • : significantly speeds up compilation if your platform supports it by making use of ,
  • : everything necessary to run Coconut's test suite,
  • : everything necessary to build Coconut's documentation, and
  • : everything necessary to develop on Coconut, including all of the dependencies above.
pipinstallcoconut[name_of_optional_dependency]
pipinstallcoocnut[opt_dep_1,opt_dep_2]

Develop Version¶

Alternatively, if you want to test out Coconut's latest and greatest, enter

which will install the most recent working version from Coconut's branch. Optional dependency installation is supported in the same manner as above. For more information on the current development build, check out the development version of this documentation. Be warned: is likely to be unstable—if you find a bug, please report it by creating a new issue.

pipinstallcoconut-develop

Usage¶

Positional Arguments¶

sourcepathtotheCoconutfile/foldertocompiledestdestinationdirectoryforcompiledfiles(defaultstothesourcedirectory)

Optional Arguments¶

-h,--helpshowthishelpmessageandexit-v,--versionprintCoconutandPythonversioninformation-tversion,--targetversionspecifytargetPythonversion(defaultstouniversal)-i,--interactforcetheinterpretertostart(otherwisestartsifnoothercommandisgiven)(implies--run)-p,--packagecompilesourceaspartofapackage(defaultstoonlyifsourceisadirectory)-a,--standalonecompilesourceasstandalonefiles(defaultstoonlyifsourceisasinglefile)-l,--line-numbers,--linenumbersaddlinenumbercommentsforeaseofdebugging-k,--keep-lines,--keeplinesincludesourcecodeincommentsforeaseofdebugging-w,--watchwatchadirectoryandrecompileonchanges-r,--runexecutecompiledPython-n,--no-write,--nowritedisablewritingcompiledPython-d,--displayprintcompiledPython-q,--quietsuppressallinformationaloutput(combinewith--displaytowriterunnablecodetostdout)-s,--strictenforcecodecleanlinessstandards--no-tco,--notcodisabletailcalloptimization-ccode,--codecoderunCoconutpassedinasastring(canalsobepipedintostdin)-jprocesses,--jobsprocessesnumberofadditionalprocessestouse(defaultsto0)(pass'sys'tousemachinedefault)-f,--forceforceoverwritingofcompiledPython(otherwiseonlyoverwriteswhensourcecodeorcompilationparameterschange)--minifyreducesizeofcompiledPython--jupyter...,--ipython...runJupyter/IPythonwithCoconutasthekernel(remainingargspassedtoJupyter)--mypy...runMyPyoncompiledPython(remainingargspassedtoMyPy)(implies--package--no-tco)--argv...setsys.argvtosourceplusremainingargsforuseinCoconutscriptbeingrun--tutorialopenCoconut's tutorial in the default web browser--documentationopenCoconut's documentation in the default webbrowser--stylenamePygmentssyntaxhighlightingstyle(or'none'todisable)(defaultstoCOCONUT_STYLEenvironmentvariable,ifitexists,otherwise'default')--recursion-limitlimit,--recursionlimitlimitsetmaximumrecursiondepthincompiler(defaultsto2000)--verboseprintverbosedebugoutput--traceprintverboseparsingdata(onlyavailableincoconut-develop)
coconut[-h][-v][-tversion][-i][-p][-a][-l][-k][-w][-r][-n][-d][-q][-s][--no-tco][-ccode][-jprocesses][-f][--minify][--jupyter...][--mypy...][--argv...][--tutorial][--documentation][--stylename][--recursion-limitlimit][--verbose][--trace][source][dest]

Coconut Scripts¶

To run a Coconut file as a script, Coconut provides the command

as an alias for

which will quietly compile and run , passing any additional arguments to the script, mimicking how the command works.

can be used in a Unix shebang line to create a Coconut script by adding the following line to the start of your script:

coconut-run<source><args>
coconut--run--quiet--targetsys<source>--argv<args>
#!/usr/bin/env coconut-run

Naming Source Files¶

Coconut source files should, so the compiler can recognize them, use the extension (preferred), , or . When Coconut compiles a (or /) file, it will compile to another file with the same name, except with instead of , which will hold the compiled code. If an extension other than is desired for the compiled files, such as for Python Processing, then that extension can be put before in the source file name, and it will be used instead of for the compiled files. For example, will compile to , whereas will compile to .

Compilation Modes¶

Files compiled by the command-line utility will vary based on compilation parameters. If an entire directory of files is compiled (which the compiler will search recursively for any folders containing , , or files), a file will be created to house necessary functions (package mode), whereas if only a single file is compiled, that information will be stored within a header inside the file (standalone mode). Standalone mode is better for single files because it gets rid of the overhead involved in importing , but package mode is better for large packages because it gets rid of the need to run the same Coconut header code again in every file, since it can just be imported from .

By default, if the argument to the command-line utility is a file, it will perform standalone compilation on it, whereas if it is a directory, it will recursively search for all (or / ) files and perform package compilation on them. Thus, in most cases, the mode chosen by Coconut automatically will be the right one. But if it is very important that no additional files like be created, for example, then the command-line utility can also be forced to use a specific mode with the () and () flags.

Compatible Python Versions¶

While Coconut syntax is based off of Python 3, Coconut code compiled in universal mode (the default ), and the Coconut compiler, should run on any Python version on the branch or on the branch.

Note: The tested against implementations are CPython and PyPy.

To make Coconut built-ins universal across Python versions, Coconut automatically overwrites Python 2 built-ins with their Python 3 counterparts. Additionally, Coconut also overwrites some Python 3 built-ins for optimization and enhancement purposes. If access to the original Python versions of any overwritten built-ins is desired, the old built-ins can be retrieved by prefixing them with .

For standard library compatibility, Coconut automatically maps imports under Python 3 names to imports under Python 2 names. Thus, Coconut will automatically take care of any standard library modules that were renamed from Python 2 to Python 3 if just the Python 3 name is used. For modules or objects that only exist in Python 3, however, Coconut has no way of maintaining compatibility.

Finally, while Coconut will try to compile Python-3-specific syntax to its universal equivalent, the following constructs have no equivalent in Python 2, and require the specification of a target of at least to be used:

  • destructuring assignment with s (use Coconut pattern-matching instead),
  • the keyword,
  • used in a context where it must be a function,
  • keyword class definition,
  • tuples and lists with unpacking or dicts with unpacking (requires ),
  • as matrix multiplication (requires ),
  • and statements (requires ), and
  • formatting strings by prefixing them with (requires ).

Allowable Targets¶

If the version of Python that the compiled code will be running on is known ahead of time, a target should be specified with . The given target will only affect the compiled code and whether or not the Python-3-specific syntax detailed above is allowed. Where Python 3 and Python 2 syntax standards differ, Coconut syntax will always follow Python 3 across all targets. The supported targets are:

  • universal (default) (will work on any of the below),
  • , (will work on any Python but ),
  • (will work on any Python but ),
  • , (will work on any Python ),
  • , (will work on any Python ),
  • (will work on any Python ),
  • (will work on any Python ),
  • (chooses the specific target corresponding to the current version).

Note: Periods are ignored in target specifications, such that the target is equivalent to the target .

Mode¶

If the (or ) flag is enabled, Coconut will throw errors on various style problems. These are

  • mixing of tabs and spaces (without will show a Warning),
  • use of imports (without will show a Warning)
  • missing new line at end of file,
  • trailing whitespace at end of lines,
  • semicolons at end of lines,
  • use of the Python-style statement,
  • inheriting from in classes (Coconut does this automatically),
  • use of to denote Unicode strings (all Coconut strings are Unicode strings), and
  • use of backslash continuations (use parenthetical continuation instead).

Additionally, disables deprecated features, making them entirely unavailable to code compiled with . It is recommended that you use the (or ) flag if you are starting a new Coconut project, as it will help you write cleaner code.

Integrations¶

Syntax Highlighting¶

Text editors with support for Coconut syntax highlighting are:

  • SublimeText: See SublimeText section below.
  • Vim: See .
  • Emacs: See .
  • Atom: See .
  • Any editor that supports Pygments (e.g. Spyder): See Pygments section below.

Alternatively, if none of the above work for you, you can just treat Coconut as Python. Simply set up your editor so it interprets all files as Python and that should highlight most of your code well enough.

SublimeText¶

Coconut syntax highlighting for SublimeText requires that Package Control, the standard package manager for SublimeText, be installed. Once that is done, simply:

  1. open the SublimeText command palette by pressing (or on Mac),
  2. type and enter , and
  3. finally type and enter .

To make sure everything is working properly, open a file, and make sure appears in the bottom right-hand corner. If something else appears, like , click on it, select at the top of the resulting menu, and then select .

Note: Coconut syntax highlighting for SublimeText is provided by the sublime-coconut package.

Pygments¶

The same command that installs the Coconut command-line utility will also install the Pygments lexer. How to use this lexer depends on the Pygments-enabled application being used, but in general simply enter as the language being highlighted and/or use a valid Coconut file extension (, , or ) and Pygments should be able to figure it out.

For example, this documentation is generated with Sphinx, with the syntax highlighting you see created by adding the line

to Coconut's .

highlight_language="coconut"

IPython/Jupyter Support¶

If you prefer IPython (the python kernel for the Jupyter framework) to the normal Python shell, Coconut can be used as a Jupyter kernel or IPython extension.

Kernel¶

If Coconut is used as a kernel, all code in the console or notebook will be sent directly to Coconut instead of Python to be evaluated. Otherwise, the Coconut kernel behaves exactly like the IPython kernel, including support for commands.

The command (or ) will launch an IPython/Jupyter notebook using Coconut as the kernel and the command (or ) will launch an IPython/Jupyter console using Coconut as the kernel. Additionally, the command (or ) will add Coconut as a language option inside of all IPython/Jupyter notebooks, even those not launched with Coconut. This command may need to be re-run when a new version of Coconut is installed.

Extension¶

If Coconut is used as an extension, a special magic command will send snippets of code to be evaluated using Coconut instead of IPython, but IPython will still be used as the default.

The line magic will load Coconut as an extension, providing the and magics and adding Coconut built-ins. The line magic will run a line of Coconut with default parameters, and the block magic will take command-line arguments on the first line, and run any Coconut code provided in the rest of the cell with those parameters.

MyPy Integration¶

Coconut has the ability to integrate with MyPy to provide optional static type-checking, including for all Coconut built-ins. Simply pass to enable MyPy integration, though be careful to pass it only as the last argument, since all arguments after are passed to , not Coconut.

Note: Since tail call optimization prevents proper type-checking, implicitly disables it.

To explicitly annotate your code with types for MyPy to check, Coconut supports Python 3 function type annotations, Python 3.6 variable type annotations, and even Coconut's own enhanced type annotation syntax. By default, all type annotations are compiled to Python-2-compatible type comments, which means all of the above works on any Python version.

Coconut even supports in the interpreter, which will intelligently scan each new line of code, in the context of previous lines, for newly-introduced MyPy errors. For example:

Note: Sometimes, MyPy will not know how to handle certain Coconut constructs, such as . In that case, simply put a comment on the Coconut line which is generating the line MyPy is complaining about (you can figure out what line this is using ) and the comment will be added to every generated line.

>>>a:str=count()[0]<string>:14:error:Incompatibletypesinassignment(expressionhastype"int",variablehastype"str")

Operators¶

In order of precedence, highest first, the operators supported in Coconut are:

Lambdas¶

Coconut provides the simple, clean operator as an alternative to Python's statements. The syntax for the operator is (or for one-argument lambdas). The operator has the same precedence as the old statement, which means it will often be necessary to surround the lambda in parentheses, and is right-associative.

Additionally, Coconut also supports an implicit usage of the operator of the form , which is equivalent to , which allows an implicit lambda to be used both when no arguments are required, and when one argument (assigned to ) is required.

Note: If normal lambda syntax is insufficient, Coconut also supports an extended lambda syntax in the form of statement lambdas.

Rationale¶

In Python, lambdas are ugly and bulky, requiring the entire word to be written out every time one is constructed. This is fine if in-line functions are very rarely needed, but in functional programming in-line functions are an essential tool.

Python Docs¶

Lambda forms (lambda expressions) have the same syntactic position as expressions. They are a shorthand to create anonymous functions; the expression yields a function object. The unnamed object behaves like a function object defined with:

Note that functions created with lambda forms cannot contain statements or annotations.

def<lambda>(arguments):returnexpression

Example¶

Coconut:

Python:

dubsums=map((x,y)->2*(x+y),range(0,10),range(10,20))dubsums|>list|>print
dubsums=map(lambdax,y:2*(x+y),range(0,10),range(10,20))print(list(dubsums))

Partial Application¶

Coconut uses a sign right after a function's name but before the open parenthesis used to call the function to denote partial application.

Coconut's partial application also supports the use of a to skip partially applying an argument, deferring filling in that argument until the partially-applied function is called. This is useful if you want to partially apply arguments that aren't first in the argument order.

Rationale¶

Partial application, or currying, is a mainstay of functional programming, and for good reason: it allows the dynamic customization of functions to fit the needs of where they are being used. Partial application allows a new function to be created out of an old function with some of its arguments pre-specified.

Python Docs¶

Return a new object which when called will behave like func called with the positional arguments args and keyword arguments keywords. If more arguments are supplied to the call, they are appended to args. If additional keyword arguments are supplied, they extend and override keywords. Roughly equivalent to:

The object is used for partial function application which “freezes” some portion of a function's arguments and/or keywords resulting in a new object with a simplified signature.

defpartial(func,*args,**keywords):defnewfunc(*fargs,**fkeywords):newkeywords=keywords.copy()newkeywords.update(fkeywords)returnfunc(*(args+fargs),**newkeywords)newfunc.func=funcnewfunc.args=argsnewfunc.keywords=keywordsreturnnewfunc

Example¶

Coconut:

Python:

expnums=range(5)|>map$(pow$(?,2))expnums|>list|>print
# unlike this simple lambda, $ produces a pickleable objectexpnums=map(lambdax:pow(x,2),range(5))print(list(expnums))

Pipeline¶

Coconut uses pipe operators for pipeline-style function application. All the operators have a precedence in-between function composition pipes and comparisons, and are left-associative. All operators also support in-place versions. The different operators are:

Additionally, all pipe operators support a lambda as the last argument, despite lambdas having a lower precedence. Thus, is equivalent to , not .

Note: To visually spread operations across several lines, just use parenthetical continuation.

Optimizations¶

It is common in Coconut to write code that uses pipes to pass an object through a series of partials and/or implicit partials, as in

which is often much more readable, as it allows the operations to be written in the order in which they are performed, instead of as in

obj|>.attribute|>.method(args)|>func$(args)|>.[index]
(|>)=>pipeforward(|*>)=>multiple-argumentpipeforward(<|)=>pipebackward(<*|)=>multiple-argumentpipebackward
===============================================Symbol(s)Associativity===============================================..n/awon't capture call**right+,-,~unary*,/,//,%,@left+,-left<<,>>left&left^left|left::n/alazya`b`cleftcaptureslambda??leftshort-circuit..>,<..,..*>,<*..n/acaptureslambda|>,<|,|*>,<*|leftcaptureslambda==,!=,<,>,<=,>=,in,notin,is,isnotn/anotunaryandleftshort-circuitorleftshort-circuitaifbelsecternaryleftshort-circuit->right===============================================


An assignment with syntax

sets the value of variable «v» to the value obtained from expression «expr».

Avoid assignment to improve transparency

Analytica doesn't allow assignment to global variables, except in a few special cases. This comes as a shock to many experienced programmers because assignment is perhaps the most common operation in conventional computer languages. But there is a good reason to avoid assignment. In conventional languages, any function or module may have the "side effect" of assigning a new value to a global variable. A global variable may be assigned a new value almost anywhere in the code. So it's hard to be sure just where it got its current value -- a major reason that software is so hard to write, understand, and debug. Computer scientists term these side effects "non-locality" or "referential opacity". Analytica avoids this problem by requiring that each global variable gets its value from a single definition, which is part of the Variable object. So you know exactly where to look if you want to understand or debug the calculation for each variable. Experienced programmers often find this ban on global assignments takes a bit of getting used to. But, they soon discover the huge advantage of improved transparency.

When Analytica allows assignment

Analytica does allow assignment in special cases that do not damage transparency unduly. You may assign to:

  • A local variable «v» that is declared in the same definition using Local, or LocalAlias, or where «v» is a parameter of the function. Since the local variable is defined and assigned in the same definition, you don't have to look far to understand its effect.
  • A Global variable in an OnClick attribute of a Button or OnChange attribute of an input variable (or in the obsolete Script attribute). The user must actively click the Button or change the Input Variable. It actually changes the definition of the variable, so the result is clear.
  • A Global variable in a user-defined Function that is called from an OnClick or OnChange attribute (or in a Function called from such a Function, and so on.)
  • An Attribute of a Global variable in the same contexts that you may assign to the value of the variable.
  • A Global Variable A in the Definition of a Global Variable B if A is defined as ComputedBy(B). This is useful if the definition of A has an algorithm that computes two (or more) results that you want to retain -- as the value of A and B. It does not damage transparency since you can immediately see in the Definition of A that it is computed in the definition of B.
  • The RandomSeed system variable.

See below for details on each of these cases.

Assignment to a Local Variable

This example shows the use of a local variable in an expression:

This expression counts the number of elements of over that are not equal to . It assigns to count. The loop also assigns consecutive values of to . Note that if has dimensions other than , this expression works fine, and count will contain these other indexes.

Assignment to a global variable in OnClick or OnChange

You may assign to a Global variable in an OnClick attribute of a Button or OnChange attribute of an input variable. You may also assign to a Global Variable in a function called from an OnClick or OnChange attribute, or a function called from such function, and so on. The rationale is that by clicking the Button or changing the input variable, the user is deliberately making a change, which may reasonably change the value and definition of a Global Variable.

Here is how to assign to a Global variable in an OnClick or OnChange Attribute (or in a Function called from such an attribute):

The value of becomes the result of calculating . To be more precise, the definition of becomes the value of .

Assignment to an Attribute

You can assign directly to any user-modifiable attribute of a global object in an expression in an OnClick or OnChange Attribute (or a function called by such an action), for example

User-modifiable attributes include Class, Identifier, Title, Units, Description, and Definition. For a Module, you can also set Authors and Filename. All these Attributes expect a text value, so the «expr» should be, or evaluate to, an atomic text value. The general syntax is:

You can remove an attribute by assigning Null to it:

Assigning a Definition

Assigning an expression to a Global variable sets its Definition to the value of the expression. For example

evaluates 10^2 to obtain 100, and sets the of to 100.

You can change the Definition to an expression, without evaluating it, by assigning the expression as a text value to the Definition:

In this case, the value of will change if changes.

So, these expressions are not equivalent:

Sets the Definition of X to the value of B
Sets the Definition of X to the text "B"
Sets the Definition of X to the expression B.

In the last case, if isn't a defined Variable, it gives a syntax error.

This table highlights the subtle differences between assigning an evaluated value to a Variable (as its new definition) and assigning a Definition directly:

Assignment Definition of X Value of X
X := 1 + 2 3 3
X := "1 + 2" "1 + 2" "1 + 2"
Definition OF X := 1 + 2 3 3
Definition OF X := "1 + 2" 1+2 3

Assigning to a Value or ProbValue

Although the Value (and ProbValue) attribute is not usually user-modifiable, you can assign a number, text, or Null directly to it, e.g.:

This may set a Value for X that is inconsistent with its Definition. So we strongly discourage assigning to the Value attribute except in unusual circumstances.

Assignment to RandomSeed

When Analytica uses pseudo-random numbers, such as when sampling from a distribution, the actual sample generated is based on the current RandomSeed, used by the internal random number generators. If you evaluate an uncertain variable at different times, or chance variables in different orders, you are likely to get different samples.

To reproduce the same sample each time, one method that can be used is to reset the random seed to a known value prior to calling the distribution function. For example,

This would generate the same sample for the normal distribution same every time it is evaluated. Analytica allows an assignment to the RandomSeed system variable from any expression, and doing so from a variable or function definition does not cause previously computed samples or results to be invalidated. However, this is the only global object that can be assigned to while a variable is being evaluated.

Assigning in a Script Attribute

You may also assign in a Script attribute, but that is an obsolete feature, replaced by OnClick or OnChange attribute. One reason the Script attribute was replaced is that it uses a slightly different syntax, known as Typescript. For example, to assign to a variable in a Script, you shoud surround the the assignment operation in parentheses, such as:

Without parentheses, a script will interpret the expression as typescript rather than as an expression. It will not evaluate the right-hand side but rather set the definition of to the literal character-for-character expression written on the right-hand side. The parentheses cause typescript to interpret the line as an expression. To avoid this subtlety, we recommend you use only the OnClick or OnChange attributes. We retain the Script attribute only for compatibility with legacy models.

To avoid this subtlety, we often recommend creating any complex button-script logic in a user-defined function, where the logic resides in a definition using the syntax most Analytica users are already well-accustomed to. Your button script can then consist of a simple single call to your user-defined function.

Gotchas with assignment

Interactions of Assignment with Array Abstraction

Analytica performs array-based operations. When an operation has side-effects, as the assignment operator does, this can have unexpected results. These three expressions may seem the same to someone accustomed to a procedural programming language, but only (C) produces the expected result. We assume they are each evaluated in Prob mode, so generate a random sample of values for result:

To understand this foible, you must realize that Analytica evaluates the If-Then-Else in an array fashion, evaluating the entire IF part, then evaluating the THEN part in an array-operation only once, and evaluating the ELSE part in an array fashion only once. It is not iterating over each atomic element of result. (We sometimes refer to this distinction has "vertical" vs. "horizontal" abstraction).

In case (A), when is evaluated (assuming Sample mode), at least one element of result is likely to satisfy , so the THEN clause will be evaluated. When this happens, the local variable is set to the scalar value of 0.5 -- it is no longer indexed by Run. Probably not what the author expected.

In case (B), will have some true and some false instances, so both the THEN and ELSE clauses will be evaluated. When THEN is evaluated, the entire value becomes 0.5 as the assignment side-effect, and the part has no real impact. Again, not what the author expected.

Case (C) does work as expected. Here the IF-THEN -else is evaluated as an array operation, returning the correct truncating, and the assignment of the entire array occurs once.

To avoid this foible, don't use assignment within a conditional THEN or ELSE clause. There are situations where you can legitimately do so, but when doing so, you should ensure that your antecedent (the IF condition) is guaranteed to be a scalar at all times.

Don't change a global index used by a transient array

You should never write a function that changes a global index (assign a new value) that might be used by a transient array. A transient array exists only temporarily during a computation, such as an array value of a local variables, a function parameter, or an intermediate result during evaluation of an expression. If you modify a Global Index with an assignment, any transient arrays using that index may be corrupted, with unpredictable consequences, including incorrect results, or a crash. Analytica does not detect this situation (which would be computationally expensive). Such are the perils of side-effects! For example, suppose is a global index, and this is in a function called by a Script:

The assignment changing makes inconsistent. Global tables that use use spliced to keep them consistent, but there is no direct link from to the transient array in local variable , so it cannot be made consistent.

Bottom line: Never change a global index while it is in-use by a value in a local variable.

Note: It is okay to assign a new value to a local index. When you do to, it essentially creates a new local index, treated as separate from the one held by local variables.

Here is an example of a transient value that doesn't involve a local variable or parameter:

is a transient array indexed by , which is held in memory as the denominator is computed. The computation of the denominator changes -- including its length. The result is inconsistent.

Slice/Subscript Assignment

(new to 4.0)

When «v» is a local variable, you may assign to a single slice of a value, leaving all other current values of the local variable unchanged. The syntax for this is:

This is only permitted when «v» is a local variable.

If you wish to change a single slice of a global variable, «X», from a button script, and if you can guarantee that every cell of the global variable contains a literal value, you can accomplish this using:

You could also accomplish this equivalently using:

For a single slice, the latter is equally efficient; however, if you have a complex algorithm that will manipulate many slices and perform many slice assignments in the process, direct assignment to slices prior to writing the value back to the global value is substantially more efficient.

Additional information on Slice Assignment is available at Subscript/Slice Operator.

Assigning an Array

If you assign an array value to a global variable (from an OnClick or OnChange attribute), e.g.

it sets the Definition of X to a Table, with specified index and values, so it looks like this:

If X was previously defined as a Table, DetermTable, ProbTable or IntraTable, it retains that form of Edit table, but using the Index(es) and values from the assigned Array.

If you want to change the form of a Table, for example from DetermTable to Table, you should first set it to Null:

If you want to change an existing Table to another form, such as DetermTable, ProbTable or IntraTable, it is best to use the MakeChangesInTable typescript command.

(new to Analytica 4.5)

If you assign a Boolean (0 or 1) to a variable defined as Checkbox(0) control, it stays as a checkbox, with the new value selected. For example:

An assignment in a user-defined function called from a Script:

changes the definition of to

Similarly, if you assign a valid value «v» to a variable defined as a Choice(i, v) control, it retains the Choice() control:

After the assignment

the definition of becomes , and the Choice menu remains.

This process only works if you assign a valid value, 1 or 0 (True or False) to a Checkbox, or a value from the index to a Choice control -- e.g. from . If you assign an invalid value that becomes the the new value of and it loses its Checkbox or Choice control, e.g.:

The new value of is with no Choice() control.

(In previous releases, the definition was always reset simply to the value assigned in all cases and it lost the control Checkbox or Choice.) This also works when assigning a new value to the cell of a Table that contains a Checkbox() or Choice():

The assignment

preserves the controls, resulting in the new edit table

Assignment to Local Variables with Handles

There are several different constructs for declaring local variables, resulting in several nuances in behavior with regard to assignment. For most users of Analytica, these nuances are unimportant -- their primary relevance is for meta-inference, where the distinctions with respect to how handles are processed becomes important. With respect to assignment, the key distinctions are what happens when you assign a value to a local variable that is current holding a handle, and whether the local variable is declared as an index or not.

In general, local variables are declared either with a declaration construct (e.g., Var..Do), or through a parameter declaration in a User-Defined Function.

When assignment is made to a local variable that currently contains a handle to another object, there are three distinct things that may happen, depending on which type of local variable is being used:

Meta-variable treatment
The local variable is changed, no longer pointing to the object, leaving the object unchanged, and causing the local variable to now contain the new value.
Alias treatment
The assignment applies to the object, changing its definition. This behavior may require the evaluation to be launched from a button script or require the object to be defined via the ComputedBy function, since it involves a global side effect.
Index treatment
The assignment creates a new local index object with the new value.

This table shows how local variables are treated by assignment based on how they are declared:

When you are performing meta-inference, it is recommended that you limit yourself to the MetaVar..Do and LocalAlias..Do constructs for declaring local variables. The Var..Do construct has been around in Analytica for a long time, long before it was widely used for meta-inference and manipulation of handles. As a result of its legacy, and the need to continue supporting backward compatibility, its treatment of handles is a less consistent, which can be a source of confusion. The LocalAlias..Do and MetaVar..Do variations were introduced for the purpose of providing two highly self-consistent, making their behavior easier to fully understand. Again, the distinctions only have a bearing when you are manipulating handles to other objects.

Examples

Var x := Handle(Va1);

x := 6

Meta-variable treatment

local becomes 6. unchanged

MetaVar x:= Handle(Va1);

x := 6

Meta-variable treatment

local becomes 6. unchanged

MetaVar x:= Handle(Va1);

x := Handle(Va2);

Meta-variable treatment

local is now holding a handle to . unchanged

LocalAlias x := Handle(Va1);

x := 6

Alias treatment

local unchanged -- still points to . 's definition changed to 6

Index I := 1..5;

Var A := I^2;

I := 10..15

Index-treatment
A new local index is created with an IndexValue of .
The local now refers to the new index.
Array is indexed by the original index, .

Suppose you have a meta-variable that points to :

and now you want to use assignment to change the definition of . Assigning to doesn't do it, since this simply changes the value of . To make the assignment, you need to force the alias treatment, which is done via LocalAlias as follows

Consider the converse, where you have a LocalAlias to :

and you want to change to now be an alias for instead. This cannot be done, because is in all ways an alias for , so any attempt to change it will be enacting a change to . What this should tell you is that if you have logic that is going to have to change what a local points to, you need to use a meta-variable (MetaVar..Do not LocalAlias..Do, or for function parameter qualifiers, Handle not Variable or Object).

Suppose you have a list of handles, and you want to set every variable in the list to 0. This can be done with a single LocalAlias statement,

This works because of array abstraction. LocalAlias is psuedo-atomic in that it points to only one item (I say "pseudo-atomic" here because the object it is an alias for may have an array, so it doesn't limit the dimensionality of the value). So since is being set to three handles, the body of LocalAlias is repeated three times, each time is an alias of a different object.

Attribute Assignment with Handles

There is no functional difference between the following two examples

In both cases, the description of is set. The second case should seem straightforward, but the first case with MetaVar appears to be setting the description attribute of rather than the description attribute of . However, it doesn't work this way because local variables are not objects -- they have no attributes themselves. Since the intent is unambiguous, the Of operator resolves a meta-variable (as well as a local alias) to the object pointed to. Thus, if you need to read or set an attribute value of the object held in your meta-variable, you don't have to obtain a local alias first.

See Also

One thought on “Non-Local Operator Assignment Is Not Guaranteed To Be Atomic Element

Leave a Reply

Your email address will not be published. Required fields are marked *