<!-- https://mathlive.io/compute-engine/guides/evaluate/ -->

# Evaluation

<Intro>
Evaluating an expression is the process of determining the value of the
expression. This involves looking up the definitions of symbols and functions,
evaluating the arguments of functions, and applying the function to the
arguments.
</Intro>

## Evaluation Methods

**To evaluate an expression**, use the `evaluate()` function.

```live
// String expression
console.log(evaluate("3^2"));

// Expression object
const expr = parse("2 + 2");
console.log(evaluate(expr));
```


:::info[Note]
The `expr.value` property does not evaluate the expression. If the expression
is a literal, it returns the literal value. If the expression is a symbol, it
looks up the symbol in the current scope and returns its value.
:::

```live
parse("x").value = 314;
console.info(parse('42').value)
console.info(parse('x').value)
console.info(parse('2 + 2').value)
```

The `N()` function provides a numeric evaluation of its argument.

```live
console.log(evaluate("2\\pi"));
console.log(N("2\\pi"));
```

:::info[Note]
The `N()` and `evaluate()` functions are shorthands for the `expr.N()` and 
`expr.evaluate()` methods. They use a common shared compute engine instance.

The `expr.N()` method is a shorthand for `expr.evaluate({numericApproximation: true})`.
:::


### Compilation

An expression can be evaluated by compiling it to JavaScript using the `compile()` function.
The result includes a `run` function that can be called to evaluate the expression.


```live
// import { compile } from '@cortex-js/compute-engine';
const result = compile('2\\pi');
console.log(result.success ? 'compiled' : 'fallback');
console.log(result.run?.());
```


<ReadMore path="/compute-engine/guides/compiling/" > 
Read more about **compiling expressions** <Icon name="chevron-right-bold" />
</ReadMore>

## Asynchronous Evaluation

Some computations can be time-consuming. For example, computing a very large
factorial. To prevent the browser from freezing, the Compute Engine can
perform some operations asynchronously.

**To perform an asynchronous evaluation**, use the `expr.evaluateAsync()` method.

```js
try {
  const fact = parse('(70!)!');
  const factResult = await fact.evaluateAsync();
  console.log(factResult);
} catch (e) {
  console.error(e);
}
```

The `expr.evaluateAsync()` method returns a `Promise` that resolves to the result
of the evaluation. It accepts the same `numericApproximation` options as `expr.evaluate()`.

It is also possible to interrupt an evaluation, for example by providing the user
with a pause/cancel button.

**To make an evaluation interruptible**, use an `AbortController`
object and a `signal`.

For example, to interrupt an evaluation after 500ms:

```js
const abort = new AbortController();
const signal = abort.signal;
setTimeout(() => abort.abort(), 500);
try {
  const fact = parse('(70!)!');
  const factResult = await fact.evaluateAsync({ signal });
  console.log(factResult);
} catch (e) {
  console.error(e);
}
```

```live
:::html
<div class="stack">
  <div class="row">
    <button id="evaluate-button">Evaluate</button>
    <button id="cancel-button" disabled>Cancel</button>
  </div>

  <div id="output"></div>
</div>
:::js
const abort = new AbortController();

document.getElementById('cancel-button').addEventListener('click', 
  () => abort.abort()
);

document.getElementById('evaluate-button').addEventListener('click', async () => {
  try {
    document.getElementById('evaluate-button').disabled = true;
    document.getElementById('cancel-button').disabled = false;

    const fact = ce.parse('(70!)!');
    const factResult = await fact.evaluateAsync({ signal: abort.signal });
    document.getElementById('output').textContent = factResult.toString();
    
    document.getElementById('evaluate-button').disabled = false;
    document.getElementById('cancel-button').disabled = true;
  } catch (e) {
    document.getElementById('evaluate-button').disabled = false;
    document.getElementById('cancel-button').disabled = true;
    console.error(e);
  }
});

```



**To set a time limit for an operation**, wrap it in a span with
`ce.withTimeLimit(limit, fn)`. The `limit` is either a number of milliseconds
or an object `{ ms, label }`; everything evaluated inside the callback shares
one deadline.

```js
try {
  const fact = ce.parse('(70!)!');
  const result = ce.withTimeLimit({ ms: 1000, label: 'my-app:eval' }, () =>
    fact.evaluate()
  );
  console.log(result);
} catch (e) {
  console.error(e);
}
```

Spans nest by taking the tighter deadline, and a `CancellationError` reports
which span's budget expired through its `attribution` and `spans` fields. See
the [Execution Constraints](/compute-engine/guides/execution-constraints/)
guide for the full model, including the iteration and recursion budgets.
(The former `ce.timeLimit` property has been removed — spans are the only
way to arm a deadline.)

When an operation is canceled either because of a timeout or an abort, a
`CancellationError` is thrown. The class can be imported from the package
to distinguish cancellations from other errors:

```js
import { CancellationError } from "@cortex-js/compute-engine";
```

The time limit is respected by long-running operations including iteration
over large or infinite collections, big operators (`Sum`, `Product`,
`Reduce`), number-theoretic functions (`Factorial`, `Totient`, `Sigma0`…),
and numeric limit extraction (`Limit`, `NLimit`).

Numerical integration is an exception: rather than throwing, Monte Carlo
integration (`NIntegrate`) returns the estimate computed from the samples
taken so far, with a correspondingly larger error bound.


## Errors

An [`["Error"]`](/compute-engine/reference/core/#Error) is a **value**, and it
propagates along the ordinary value path. If an operand that a function
evaluates strictly turns out to be an error, the whole expression evaluates to
that error — not to a frozen expression wrapped around it. The propagated
error carries an `["ErrorTrace"]` breadcrumb recording the operators it passed
through, so the failure site is still recoverable.

```ts
console.log(ce.parse('\\ln(\\text{a}) + 2').evaluate().json);
// ➔ ["Error",
//      ["ErrorCode", "'incompatible-type'", "'number'", "'string'"],
//      ["ErrorTrace", ["ErrorFrame", "'Ln'", 1], ["ErrorFrame", "'Add'", 1]]]
```

A **collection** is the exception: an error among its elements stays in place,
because a collection containing an error is still a well-formed collection.

Errors do not spread past the tools that inspect them. `Type` reports
`"error"`, [`IsError`](/compute-engine/reference/core/#IsError) answers
`True`/`False`, and
[`Match`](/compute-engine/reference/control-structures/#Match) decides on an
error subject — an `["Error", ...]` case destructures it, which is how a
failure is rescued.

`NaN` is **not** an error. It is an ordinary IEEE numeric value that inhabits
the number domain, so it does not propagate this way: a function applied to
`NaN` runs and receives it, and is free to inspect it.


## Lexical Scopes and Evaluation Contexts

The Compute Engine supports
[lexical scoping](<https://en.wikipedia.org/wiki/Scope_(computer_science)>).

A **lexical scope** is a region of the code where a symbol is defined. 
Each scope has its own bindings table, which is a
mapping of symbols to their definitions. The definition includes the type
of the symbol, whether it is a constant and other properties.


An **evaluation context** is a snapshot of the current state of the Compute 
Engine. It includes the values of all symbols currently in scope and 
a chain of lexical scopes.

Evaluation contexts are arranged in a stack, with the current (top-most) 
evaluation context available with `ce.context`.

Evaluation contexts are created automatically, for example when a new scope is
created, or each time a recursive function is called.

Some functions may create their own scope. These functions have the `scoped`
flag set to `true`. For example, the `Block` function creates a new scope
when it is called: any declarations made in the block are only visible within the
block. Similarly, the `Sum` function creates a new scope so that the index
variable is not visible outside the sum.

### Binding

**[Name Binding](https://en.wikipedia.org/wiki/Name_binding) is the process of
associating a symbol with a definition.**

Name Binding should not be confused with **value binding** which is the process
of associating a **value** to a symbol.


To bind a symbol to a definition, the Compute Engine looks up the bindings table
of the current scope. If the symbol is not found in the table, the parent 
scope is searched, and so on until a definition is found.

If no definition is found, the symbol is declared with a type of
`unknown`, or a more specific type if the context allows it.

If the symbol is found, the definition record is used to evaluate the
expression. 

The definition record contains information about the symbol, such as its
type, whether it is a constant, and how to evaluate it.

There are two kind of definition records:
1. **Value Definition Record**: This record contains information about the
   symbol, such as its type and whether it is a constant.
2. **Operator Definition Record**: This record contains information about the
   operator, such as its signature and how to evaluate it.

Name binding is done during canonicalization. If name binding failed, the
`isValid` property of the expression is `false`.

**To get a list of the errors in an expression** use the `expr.errors` property.

<ReadMore path="/compute-engine/guides/expressions/#errors" > Read more about the
<strong>errors</strong> <Icon name="chevron-right-bold" /></ReadMore>

### Bound Variables and Assigned Values

Some operators **bind** a variable of their own: `D`, `Integrate`, `Limit`,
`Sum`, `Product`, `Solve`, and `Function` (its parameters), among others. The
variable such an operator binds is a **pure symbol** — only its declared type
and any in-scope assumptions apply. A **value** assigned to a same-named symbol
does **not** apply to it, and the variable stays symbolic in the result.

```js
ce.assign('x', 5);
ce.parse('\\frac{d}{dx} x^2').evaluate().print();
// ➔ 2x        (not 10 — the bound "x" ignores its assigned value)

ce.parse('\\int x^2 \\,dx').evaluate().print();
// ➔ x^3/3     (not 125/3)
```

Every **other** symbol in the expression is **free**: its value always applies
under evaluation. So with `a := 3` as well, `\frac{d}{dx}(a\,x^2)` is `6x` —
the free `a` resolves, the bound `x` does not.

To use an assumption instead of a value (which *does* inform simplification and
solving), use `ce.assume()` rather than `ce.assign()`.

### Default Scopes

The Compute Engine has a set of default scopes that are used to look up
symbols. These scopes are created automatically when the Compute Engine
is initialized. The default scopes include the **system** scope, and the **global** scope.

The **system** scope contains the definitions of all the built-in functions and
operators. The **global** scope is initially empty, but can be used to
store user-defined functions and symbols.

The **global** scope is the default scope used when the Compute Engine is
initialized.

Additional scopes can be created using the `ce.pushScope()` method.


### Creating New Scopes

**To add a new scope** use `ce.pushScope()`.

```ts
ce.assign('x', 100); // "x" is defined in the current scope
ce.pushScope();
ce.assign('x', 500); // "x" is defined in the new scope
console.log(ce.expr('x')); // 500
ce.popScope();
console.log(ce.expr('x')); // 100
```

**To exit a scope** use `ce.popScope()`.

This will invalidate any definitions associated with the scope, and restore the
symbol table from previous scopes that may have been shadowed by the current
scope.


## Evaluation Loop

:::info

This is an advanced topic. You don't need to know the details of how the
evaluation loop works, unless you're interested in extending the standard
library and providing your own function definitions.

:::

Each symbol is **bound** to a definition within a **lexical scope** during 
canonicalization. This usually happens when calling `ce.expr()` or `ce.parse()`, 
or if accessing the `.canonical` property of a
non-canonical expression.

When a function is evaluated, the following steps are followed:

1. If the expression is not canonical, it cannot be evaluated and an error is
   thrown. The expression must be canonicalized first.

2. Each argument of the function are evaluated, left to right, unless the 
   function has a `lazy` flag. If the function is lazy, the arguments are not
   evaluated.

   1. An argument can be **held**, in which case it is not evaluated. Held
      arguments can be useful when you need to pass a symbolic expression to a
      function. If it wasn't held, the result of evaluating the expression would
      be used, not the symbolic expression.

      Alternatively, using the `Hold` function will prevent its argument from
      being evaluated. Conversely, the `ReleaseHold` function will force an
      evaluation.

   2. If an argument is a `["Sequence"]` expression, treat each argument of the
      sequence expression as if it was an argument of the function. If the
      sequence is empty, ignore the argument.

3. If the function is associative, flatten its arguments as necessary. \\[
   f(f(a, b), c) \to f(a, b, c) \\]

4. Apply the function to the arguments

5. Return the result in canonical form.
