Home Explore Blog CI



nushell

2nd chunk of `book/how_nushell_code_gets_run.md`
264cac093b0ae46f0d2ec95f33f56a4ea3a9ce41f56e873a0000000100000fdf
One big difference, however, between interpreted and compiled languages is that interpreted languages typically implement an _`eval` function_ while compiled languages do not. What does this mean?
:::

## Dynamic vs. Static Languages

::: tip Terminology
In general, the difference between a dynamic and static language is how much of the source code is resolved during Compilation (or Parsing) vs. Evaluation/Runtime:

- _"Static"_ languages perform more code analysis (e.g., type-checking, [data ownership](https://doc.rust-lang.org/stable/book/ch04-00-understanding-ownership.html)) during Compilation/Parsing.

- _"Dynamic"_ languages perform more code analysis, including `eval` of additional code, during Evaluation/Runtime.

For the purposes of this discussion, the primary difference between a static and dynamic language is whether or not it has an `eval` function.

:::

### Eval Function

Most dynamic, interpreted languages have an `eval` function. For example, [Python `eval`](https://docs.python.org/3/library/functions.html#eval) (also, [Python `exec`](https://docs.python.org/3/library/functions.html#exec)) or [Bash `eval`](https://linux.die.net/man/1/bash).

The argument to an `eval` is _"source code inside of source code"_, typically conditionally or dynamically computed. This means that, when an interpreted language encounters an `eval` in source code during Parse/Eval, it typically interrupts the normal Evaluation process to start a new Parse/Eval on the source code argument to the `eval`.

Here's a simple Python `eval` example to demonstrate this (potentially confusing!) concept:

```python:line-numbers
# hello_eval.py

print("Hello, World!")
eval("print('Hello, Eval!')")
```

When you run the file (`python hello_eval.py`), you'll see two messages: _"Hello, World!"_ and _"Hello, Eval!"_. Here is what happens:

1. The entire program is Parsed
2. (Line 3) `print("Hello, World!")` is Evaluated
3. (Line 4) In order to evaluate `eval("print('Hello, Eval!')")`:
   1. `print('Hello, Eval!')` is Parsed
   2. `print('Hello, Eval!')` is Evaluated

::: tip More fun
Consider `eval("eval(\"print('Hello, Eval!')\")")` and so on!
:::

Notice how the use of `eval` here adds a new "meta" step into the execution process. Instead of a single Parse/Eval, the `eval` creates additional, "recursive" Parse/Eval steps instead. This means that the bytecode produced by the Python interpreter can be further modified during the evaluation.

Nushell does not allow this.

As mentioned above, without an `eval` function to modify the bytecode during the interpretation process, there's very little difference (at a high level) between the Parse/Eval process of an interpreted language and that of the Compile/Run in compiled languages like C++ and Rust.

::: tip Takeaway
This is why we recommend that you _"think of Nushell as a compiled language"_. Despite being an interpreted language, its lack of `eval` gives it some of the characteristic benefits as well as limitations common in traditional static, compiled languages.
:::

We'll dig deeper into what it means in the next section.

## Implications

Consider this Python example:

```python:line-numbers
exec("def hello(): print('Hello eval!')")
hello()
```

::: note
We're using `exec` in this example instead of `eval` because it can execute any valid Python code rather than being limited to `eval` expressions. The principle is similar in both cases, though.
:::

During interpretation:

1. The entire program is Parsed
2. In order to Evaluate Line 1:
   1. `def hello(): print('Hello eval!')` is Parsed
   2. `def hello(): print('Hello eval!')` is Evaluated
3. (Line 2) `hello()` is evaluated.

Note, that until step 2.2, the interpreter has no idea that a function `hello` even exists! This makes [static analysis](https://en.wikipedia.org/wiki/Static_program_analysis) of dynamic languages challenging. In this example, the existence of the `hello` function cannot be checked just by parsing (compiling) the source code. The interpreter must evaluate (run) the code to discover it.

Title: Dynamic vs. Static Languages: The Eval Function and Its Implications
Summary
This section delves into the concept of the `eval` function in dynamic, interpreted languages like Python and Bash, explaining how it allows for the execution of source code within source code, leading to recursive parsing and evaluation steps. It uses a Python example to illustrate this process and highlights how Nushell's lack of an `eval` function makes it behave more like a compiled language, enabling static analysis. It then discusses the implications of dynamic code execution, such as the ability to define functions at runtime.