diff --git a/src/main.md b/src/main.md
index f943500..d9e9caf 100644
--- a/src/main.md
+++ b/src/main.md
@@ -1,27 +1,29 @@
-
-## ABSTRACT
-
-This paper proposes $\lambda_{mmm}$:a call-by-value simply-typed lambda calculus-based intermediate representation of programming languages for music that deal with synchronous signal processing, as well as a virtual machine and instruction set to run $\lambda_{mmm}$. Digital signal processing can be represented with a syntax that incorporates the internal states of delay and feedback into the lambda calculus. $\lambda_{mmm}$ is defined as a superset of the lambda calculus, which allows users to construct a generative signal processing graph and its execution in an identical semantics. On the other hand, due to its specification, a problem was found that when dealing with higher-order functions, the users have to determine whether they are executed in the global environment evaluation or in the DSP execution, and a solution was proposed by introducing multi-stage computation.
+---
+abstract: |
+ This paper proposes $\lambda_{mmm}$, a call-by-value, simply typed lambda calculus-based intermediate representation for a music programming language that handles synchronous signal processing and introduces a virtual machine and instruction set to execute $\lambda_{mmm}$. Digital signal processing is represented by a syntax that incorporates the internal states of delay and feedback into the lambda calculus. $\lambda_{mmm}$ extends the lambda calculus, allowing users to construct generative signal processing graphs and execute them with consistent semantics. However, a challenge arises when handling higher-order functions because users must determine whether execution occurs within the global environment or during DSP execution. This issue can potentially be resolved through multi-stage computation.
+---
# Introduction {#sec:intro}
-A number of programming languages for sound and music have been developed to date, but not many have strongly formalised semantics. One language that is both rigorously formalised and practical is Faust [@Orlarey2004], which combines blocks with inputs and outputs with five primitive operations - parallel, sequential, split, merge and recursive connection. By having basic arithmetics and delay as a primitive block, any type of signal processing can be written in Faust. In a later extension, macros based on a term rewriting system has introduced that allows the higher abstraction of systems with an arbitrary number of inputs and outputs [@graf2010].
+Many programming languages have been developed for sound and music; however, only a few possess strongly formalized semantics. A language that is both rigorously formalized and practical is Faust [@Orlarey2004]; it combines blocks with inputs and outputs with five primitive operations: parallel, sequential, split, merge, and recursive connection. Almost any type of signal processing can be written in Faust by providing basic arithmetic, conditionals, and delays as primitive blocks. In a later extension, a macro based on a term rewriting system was introduced that allowed users to parameterize blocks with an arbitrary number of inputs and outputs [@graf2010].
-This strong abstraction capability through formalisation enables Faust to be translated to various backends, such as C++ and Rust. On the other hand, BDA lacks theoretical and practical compatibility with common programming languages: although it is possible to call external C functions in Faust, the assumption is basically pure function calls without memory allocation and deallocation. Therefore, while it is easy to embed Faust in another language, it is not easy to call another language from Faust.
+This strong abstraction capability through formalization enables Faust to be translated into various backends, such as C, C++, Rust, and LLVM IR. On the other hand, Faust's Block Diagram Algebra (BDA) lacks theoretical and practical compatibility with common programming languages. Although it is possible to call external C functions in Faust, these functions are assumed to be pure functions that do not have internal states. Therefore, while it is easy to embed Faust in another language, it is not easy to call another language from Faust.
-In addition, macros in Faust are independent as a term rewriting system that generates BDA based on pattern matching. Therefore, even though the distinction between real and integer types does not exist in BDA, the arguments for pattern matching implicitly required to be an integer, which causes compile errors. This implicit typing rules are not intuitice for novice users.
+In addition, a macro for Faust is an independent term rewriting system that generates a BDA based on pattern matching. Consequently, the numeric arguments for pattern matching are implicitly required to be integers, which can sometimes lead to compile-time errors despite the fact that BDA does not distinguish between real and integer types. However, the implicit typing rules are not intuitive for novice users.
-Proposing a computational model for signal processing that is based on the more generic computational models, such as the lambda calculus has the potential to interoperate between many different general-purpose languages, and also facilitate the appropriation of existing optimisation methods and the implementation of compilers and run-time.
+Proposing a computational model for signal processing based on more generic computational models such as lambda calculus has the potential to enable interoperability between many different general-purpose languages and facilitate the appropriation of existing optimization methods and the implementation of compilers and runtimes.
-Currently, it has been proved that BDA can be converted to a general-purpose functional language in the form of using Arrows, a higher-level abstraction of monads[@gaster2018]. But higher-order functions on general-purpose functional languages are often implemented on the basis of dynamic memory allocation and release, which makes it difficult to use them in host languages for the real-time signal processing.
+It has been demonstrated that BDA can be converted into a general-purpose functional language using an arrow, which is a higher-level abstraction of monads [@gaster2018]. However, higher-order functions in general-purpose functional languages are often implemented using dynamic memory allocation and deallocation, making them difficult to use in host languages designed for real-time signal processing.
-Also, Kronos [@norilo2015] and W-calculus [@arias2021] are examples of attempts at lambda calculus-based abstraction, while being influenced by Faust. Kronos is based on the theoretical foundation of System-$F\omega$, a lambda computation in which the type itself is the object of the lambda abstraction (a function can be defined that computes the type and returns a new type). The W-calculus limits the systems it represents to linear time-invariant ones and defines a more formal semantics, aiming at automatic proofs of the linearlity and an identity of graph topologies.
+In addition, Kronos [@norilo2015] and W-calculus [@arias2021] are examples of lambda calculus-based abstractions influenced by Faust. Kronos is based on the theoretical foundation of the System-$F\omega$, a variation of lambda calculus in which the types themselves can be abstracted (i.e., a function that takes a type as input and returns a new type can be defined). In Kronos, type calculations correspond to signal graph generation, whereas the value calculations correspond to actual processing. Delay is the only special primitive operation in Kronos, and feedback routing can be represented as a recursive function application in type calculations.
-Previously, the author designed a programming language for music *mimium* [@matsuura2021a]. It adds the basic operations of delay and feedback to lambda-calculus, signal processing can be expressed concisely while having a syntax close to that of general-purpose programming languages(especially, the syntax of *mimium* is designed to be looks like Rust language).
+W-calculus includes feedback as a primitive operation, along with the ability to access the value of a variable from the past (i.e., delay). W-calculus restricts systems to those that can represent linear-time-invariant processes, such as filters and reverberators, and defines more formal semantics, aiming for automatic proofs of linearity and the identity of graph topologies.
-One of the previous issues with *mimium* was the inability to compile the codes which contains a combination of recursive or higher-order functions and stateful functions because the compiler can not be determine the data size of the internal state of the signal processing.
+Previously, the author designed the music programming language *mimium* [@matsuura2021a]. By incorporating basic operations such as delay and feedback into the lambda calculus, signal processing can be concisely expressed while maintaining a syntax similar to that of general-purpose programming languages. Notably, *mimium*'s syntax was designed to resemble the Rust programming language.
-In this paper, the syntax and semantics of $\lambda_{mmm}$, a superset of the call-by-value simply-typed lambda calculus are explained, as a computation model that is supposed to be an intermediate representation of *mimium*. Also, a virtual machine and its instruction set are proposed to execute this computation model practically. Lastly, a space for optimization for running *mimium* using $\lambda_{mmm}$will be discussed.
+An earlier issue with *mimium* was its inability to compile code that contained combinations of recursive or higher-order functions with stateful functions involving delay or feedback because the compiler could not determine the data size of the internal state used in signal processing.
+
+In this paper, I propose the syntax and semantics of $\lambda_{mmm}$, an extended call-by-value simply typed lambda calculus, as a computational model intended to serve as an intermediate representation for *mimium*[^1]. In addition, I propose a virtual machine and its instruction set, based on Lua's VM, to execute this computational model in practice. Finally, I discuss both the challenges and potential of the current model, one of which is that users must differentiate whether a calculation occurs in a global context or during actual signal processing; the other is that runtime interoperability with other programming languages could be easier than in existing DSP languages.
# Syntax {#sec:syntax}
@@ -68,12 +70,13 @@ $$
Figure 1. Definition of Types, Values and Terms of the λmmm(Basic arithmetics are omitted).
+The types and terms of $\lambda_{mmm}$ are presented in Figure [1](#fig:syntax_v){reference-type="ref" reference="fig:syntax_v"}.
-Definition of types and terms of the $\lambda_{mmm}$ are shown in Figure [1](#fig:syntax_v).
+Two terms are introduced in addition to the standard simply typed lambda calculus: $delay\ n\ e_1\ e_2$, which refers to the previous value of $e_1$ by $e_2$ samples (with a maximum delay of $n$ to limit memory usage to a finite size), and $feed\ x.e$, an abstraction that allows the user to refer to the result of evaluating $e$ from one time unit earlier as $x$ during the evaluation of $e$ itself.
-Two terms are added in addition to the usual simply-typed lambda calculus, $delay e_1 e_2$ that refers a previous value of $e_1$ to $e_2$ sample before, and $feed x.e$ abstraction that the user can refer the result of the evaluation of the $e$ one unit time before as $x$ during the evaluation of $e$ itself.
+## Syntactic Sugar of the Feedback Expression in *mimium* {#sec:mimium}
-## Syntactic sugar of the feedback expression in *mimium* {#sec:mimium}
+The programming language *mimium*, developed by the author, includes a keyword $self$ that can be used in function definitions to refer to the previous return value of the function. An example of a simple one-pole filter function, which mixes the input and last output signals such that the sum of the input and feedback gains is 1, is shown in Listing [\[lst:onepole\]](#lst:onepole){reference-type="ref" reference="lst:onepole"}. This code can be expressed in as illustrated in Figure [2](#fig:onepole){reference-type="ref" reference="fig:onepole"}.
```rust
fn onepole(x,g){
@@ -82,8 +85,6 @@ fn onepole(x,g){
```
*Listing 1. Example of the code of one-pole filter in mimium.*
-*mimium* by the author has a keyword $self$ that can be used in function definition, that refers to the previous return value of the function. The example code of the simple one-pole filter function is shown in Listing [1](#lst:onepole). This code can be expressed in $\lambda_{mmm}$ as Figure [2](#fig:onepole){reference-type="ref" reference="fig:onepole"}.
-
$$
\begin{aligned}
let\ & onepole = \\
@@ -93,7 +94,7 @@ $$
Equivalent expression to Listing [lst:onepole] in λmmm.
-## Typing Rule {#sec:typing}
+## Typing Rules {#sec:typing}
$$
\begin{gathered}
@@ -112,17 +113,16 @@ $$
-Additional typing rules to usual simply-typed lambda calculus are shown in Figure [3](#fig:typing).
+Additional typing rules for typical simply typed lambda calculus are shown in Figure [3](#fig:typing){reference-type="ref" reference="fig:typing"}.
-As primitive types, there are a real number type to used in most of signal processing and a natural number type that is used to indice of delay.
+The primitive types include a real number type, used in most signal processing, and a natural number type, used for the indices of delay.
-In the W-calculus, a starting point of designing $\lambda_{mmm}$, function types can takes tuples of real numbers and return tuples of real numbers. This means that higher-order functions cannot be written. While this restriction is reasonable as a design choice for a language for signal processing since higher-order functions require data structures that require dynamic memory allocation, such as closures, for their implementation, it also lacks the generality of the lambda calculus.
+In W-calculus, which directly inspired the design of $\lambda_{mmm}$, the function types can only take tuples of real numbers and return tuples of real numbers. This restriction prevents the definition of higher-order functions. While this limitation is reasonable for a signal processing language---since higher-order functions require data structures such as closures that depend on dynamic memory allocation---it also reduces the generality of lambda calculus.
-In $\lambda_{mmm}$, the problem of memory allocation for closures is left to the implementation of the runtime in the Section[4](#sec:vm), and higher-order functions are allowed. However, the $feed$ abstraction does not allow function types as its input and output. Allowing the return of function types in the $feed$ abstraction means that it is possible to define functions whose processing contents change time-to-time. While this may be interesting theoritically, there are currently no practical cases in real-world signal processing, and it is expected to further complicate implementations.
+In $\lambda_{mmm}$, the problem of memory allocation for closures is delegated to runtime implementation (see Section [4](#sec:vm){reference-type="ref" reference="sec:vm"}), which allows the use of higher-order functions. However, $feed$ abstraction does not permit function types to be either input or output. Allowing function types in the $feed$ abstraction enables the definition of functions whose behavior could change over time. While this is theoretically interesting, there are no practical examples in real-world signal processing, and such a feature would likely further complicate the implementation.
# Semantics {#sec:semantics}
-
$$
\begin{gathered}
&
@@ -145,47 +145,43 @@ $$
\end{gathered}
$$
-The excerpt of operational semantics of the $\lambda_{mmm}$is shown in Figure [\[fig:semantics\]](#fig:semantics){reference-type="ref" reference="fig:semantics"}. This big-step semantics is a conceptual explanation of the evaluation that, when the current time is $n$, the previous evaluation environment $t$ samples before can be referred to as $E^{n-t}$ , and that when the time \< 0, the evaluation of any term is evaluated to the default value of its type (0 for the numeric types).
+An excerpt of the operational semantics for $\lambda_{mmm}$ is shown in Figure [x](#fig:semantics){reference-type="ref" reference="fig:semantics"}. This big-step semantics conceptually explains the evaluation process. When the current time is $n$, the evaluation environment from $t$ prior samples can be referred to as $E^{n-t}$. If the time is less than 0, any term is evaluated as the default value of its type (0 for numeric types).
-Of course, if we tried to execute this semantics in a straightforward manner, we would have to redo the calculation from time 0 to the current time every sample, with saving all the variable environments at each sample. In practice, therefore, a virtual machine is defined that takes into account the internal memory space used by delay and feed, and the $\lambda_{mmm}$terms are converted into instructions for that machine before execution.
+Naturally, if we attempt to execute these semantics directly, we would need to recalculate from time 0 to the current time for every sample, saving all the variable environments at each step. However, in practice, a virtual machine is defined to account for the internal memory space used by $delay$ and $feed$, and terms are compiled into instructions for this machine before execution.
# VM Model and Instruction Set {#sec:vm}
-A model for the virtual machine and its instruction set to run $\lambda_{mmm}$ is based on Lua version 5.0[@ierusalimschy2005].
+The virtual machine (VM) model and its instruction set for running $\lambda_{mmm}$ are based on Lua version 5 VM [@ierusalimschy2005].
-When executing a computational model based on lambda calculus, the problem is how to handle a data structure called a closure that captures the variable environment where the inner function is defined, to refer the outer variables from the inner function context. If the dictionary data of names and values of variables are paired with inner function, implementation of the compiler(intepreter) is simple, but run-time performance is limited.
+A key challenge when executing a computational model based on lambda calculus is handling the data structure, which is known as a closure. A closure captures the variable environment in which the inner function is defined, allowing it to refer to the variables from the outer function's context. If the inner function is paired with a dictionary of variable names and values, the compiler (or interpreter) implementation is straightforward; however, the runtime performance is limited.
-On the contrary, a runtime performance can be improved by performing a process called closure transformation (or lambda lifting), which analyses all the names of outer variables referred by the inner function and transforms the inner function by adding argument so that the variables can be referred explicitly, but the compiler implementation of the transformation is relatively complex.
+Conversely, the runtime performance can be improved using a process called closure conversion (or lambda lifting). This process analyzes all the outer variables referenced by the inner function and transforms the inner function by adding arguments; thus, the outer variables can be referred to explicitly. However, the implementation of this transformation in the compiler is relatively complex.
-The Lua VM takes an intermediate approach between these two by adding the VM instructions `GETUPVALUE` /
-`SETUPVALUE`, which allows the outer variables to be referenced dynamically at runtime. The implementation of compiler and VM using *Upvalue* is simpler than closure conversion, while at the same time preventing execution performance degradation, as outer variables can be referenced via the call stack rather than on the heap memory unless the closure object escapes from the context of the original function[@nystrom2021].
+The Lua VM adopts a middle-ground approach between these two methods by adding the VM instructions `GETUPVALUE` and `SETUPVALUE`, which allow the outer variables to be dynamically referenced at runtime. The implementation of the compiler and VM using *upvalues* is simpler than full closure conversion while still avoiding significant performance degradation. In this approach, the outer variables are accessed via the call stack rather than the heap memory unless the closure escapes the context of the original function [@nystrom2021].
-Also, Upvalue helps interoperations between other programming languages, as Lua can be easily embedded through C language API and when implementing external libraries in C, programmer can access to upvalues of Lua Runtime not only the stack values in C API.
+In addition, *upvalues* facilitate interoperability with other programming languages. Lua can be easily embedded through its C API, and when implementing external libraries in C, programmers can access the upvalues of the Lua runtime, not just the stack values available via the C API.
## Instruction Set {#sec:instruction}
-VM Instructions for $\lambda_{mmm}$ differs from Lua VM in the following respects.
+The VM instructions for $\lambda_{mmm}$ differ from those for the Lua VM in the following aspects:
-1. Since *mimium* is a statically typed language unlike Lua, instructions for basic arithmetics are provided for each type.
+1. Since mimium is a statically typed language, unlike Lua, instructions for basic arithmetic operations are provided for each type[^2].
-2. The call operation is separated into the normal function call and the call of closure to handle higher-order statefull functions(See [4.2](#sec:vmstructure){reference-type="ref" reference="sec:vmstructure"} for details).
+2. The call operation is split into normal function calls and closure calls owing to the static typing and to manage higher-order stateful functions (see [4.2](#sec:vmstructure){reference-type="ref" reference="sec:vmstructure"} for details).
-3. If statements are realised by a combination of two instructions, `JMP` and `JMPIFNEG`, whereas the Lua VM uses a dedicated `TEST` instruction.
+3. Conditional statements are implemented using a combination of two instructions, `JMP` and `JMPIFNEG`, whereas the Lua VM employs a dedicated `TEST` instruction.
-4. Instructions related to for loop, the `SELF` instruction used for object-oriented programming and the `TABLE`-related instructions for metadata references to variables are omitted in *mimium* as they are not used.
+4. Instructions related to for-loops, the `SELF` instruction used in object-oriented programming, and the `TABLE`-related instructions for metadata references to variables are omitted in mimium as they are unnecessary.
-5. Instructions related to list-related data structures are also omitted in this paper, as the implementation of data structures such as tuples and arrays was omitted in the description of the $\lambda_{mmm}$ in this paper.
+5. Instructions related to list-like data structures are also excluded from this paper, as the implementation of data structures such as tuples and arrays is outside the scope of the $\lambda_{mmm}$ description here.
-Instructions in $\lambda_{mmm}$ VM are 32bit data with operation tag and 3 operands. Currently, a bit width for the tag and each operands are all 8 bit[^1].
+The VM for $\lambda_{mmm}$ operates as a register machine similar to the Lua VM (post version 5). However, unlike traditional register machines, it does not employ physical registers. Instead, the register number simply refers to an offset index on the call stack relative to the base pointer during VM execution. The first operand of most instructions specifies the register number where the result of the operation is stored.
-The VM of $\lambda_{mmm}$ is a register machine like the Lua VM after version 5, although the VM has no real register but the register number simply means the offset index of the call stack from the base pointer at the point of execution of the VM. The first operand of most instructions is the register number in which to store the result of the operation.
+A list of instructions is presented in Figure [\[fig:instruction\]](#fig:instruction){reference-type="ref" reference="fig:instruction"} (basic arithmetic operations are partially omitted). The notation for the instructions follows the format outlined in the Lua VM documentation [@ierusalimschy2005 p.13]. The operation name, list of operands, and pseudocode of the operation are displayed from left to right. When each of the three operands is used as an unsigned 8-bit integer, it is represented as `A B C`. If an operand is used as a signed integer, then it is prefixed with `s`. When the two operand fields are combined into a 16-bit value, the suffix `x` is added. For example, when `B` and `C` are merged and treated as a signed 16-bit value, they are represented as `sBx`.
-The list of instructions is shown in Figure [\[fig:instruction\]](#fig:instruction) (basic arithmetic operations are partly omitted). The notation for the instruction follows the Lua VM paper [@ierusalimschy2005 p.13]. From left to right, the name of operation, a list of operands, and pseudo-code of the operation. When using each of the three operands as unsigned 8 bits, they are denoted as `A B C`. When used with a signed integer, prefix `s` is added, and when the two operand fields are used as one 16 bits, an suffix `x` is added. For example, when B and C are merged and treated as signed 16 bits, they are denoted as `sBx`.
+In the pseudocode, `R(A)` denotes the data being moved in and out of the register (or call stack) at the base pointer + `A` for the current function. `K(A)` refers to the `A`-th entry in the static variable section of the compiled program, and `U(A)` accesses the `A`-th upvalue of the current function.
-In pseudo-code describing an functionality, `R(A)` means that data is moved in and out through the register (call stack) according to the numerical value of the operand `A`. `K(A)` means that it retrieves the `A`-th number in the static variable field of the compiled programm. `U(A)` means that referring `A`-th Upvalue of the current function.
-
-In addition to Lua's Upvalue operation, 4 operations related to internal state variables over time, `GETSTATE`, `SETSTATE`,\
-`SHIFTSTATE` and `DELAY` are added.
+In addition to Lua's upvalue operations, four new operations--- `GETSTATE`, `SETSTATE`, `SHIFTSTATE`, and `DELAY` ---have been introduced to handle the compilation of the $delay$ and $feed$ expressions in $\lambda_{mmm}$.
```
MOVE A B R(A) := R(B)
@@ -213,189 +209,275 @@ In addition to Lua's Upvalue operation, 4 operations related to internal state v
...Other basic arithmetics continues for each primitive types...
```
-## Overview of the VM structure {#sec:vmstructure}
+## Overview of the VM Structure {#sec:vmstructure}
-The overview of a data structure of the VM, program and the instantiated closure for $\lambda_{mmm}$ is shown in Figure [\[fig:vmstructure\]](#fig:vmstructure){reference-type="ref" reference="fig:vmstructure"} . In addition to the normal call stack, the VM has a storage area for managing internal state data for feedback and delay.
+The overall structure of the virtual machine program, and instantiated closures for $\lambda_{mmm}$ is depicted in Figure [\[fig:vmstructure\]](#fig:vmstructure){reference-type="ref" reference="fig:vmstructure"}. In addition to the usual call stack, the VM has a dedicated storage area (a flat array) to manage the internal state data for feedback and delay.
-This storage area is accompanied by data indicating the position from which the internal state is retrieved by the `GETSTATE` / `SETSTATE` instructions. This position is modified by `SHIFTSTATE` operation. The the actual data in the state storage memory are statically layed out at compile time by analyzing function calls that include references to `self`, call of `delay` and the functions which will call such statefull functions recursively.
+This storage area is accompanied by pointers that indicate the positions from which the internal state data are retrieved via the
+`GETSTATE` and `SETSTATE` instructions. These positions are shi-fted forward or backward using the `SHIFTSTATE` instruction. The actual data layout in the state storage memory is statically determined during compilation by analyzing function calls involving references to `self`, `delay`, and other stateful functions, including those that recursively invoke such functions. The `DELAY` operation takes two inputs: `B`, representing the input value, and `C`, representing the delay time in the samples.
-However, in the case of higher-order functions that receive a function as an argument and return another function, the layout of the internal state of the given function is unknown at the compilation, so an internal state storage area is created for each instantiated closure separately from the global storage area held by the VM instance itself. The VM switches the `State_Ptr`, which points the internal state storage to be used, at each closure call, to the storage area on the closure, and returns a pointer pointing to the global storage area each time the closure context ends.
+However, for higher-order functions---functions that take another function as an argument or return one---the internal state layout of the passed function is unknown at compile time. Consequently, a separate internal state storage area is allocated to each instantiated closure, which is distinct from the global storage area maintained by the VM instance. The VM also uses an additional stack to keep track of the pointers in the state storage of instantiated closures. Each time a `CALLCLS` operation is executed, the VM pushes the pointer from the state storage of the closure onto the state stack. Upon completing the closure call, the VM pops the state pointer off the stack.
-Instantiated closures also hold the storage area of Upvalues. Until the closure exits the context of the parent function ("Open Closure"), Upvalues holds a negative offset on the stack at the ongoing execution. Because this offset value can be determined at compile time, stored in the function prototype in the program. For instance, if the Upvalue indexes in the program were `[4,3]`, `GETUPVALUE 6 1` means that, take `3` from the upvalue indexes and get value from `R(-2)` (3-5) over the base pointer and store it to `R(6)`.
+Instantiated closures also maintain their own storage areas for upvalues. Until a closure exits the context of its parent function (known as an "Open Closure"), its upvalues hold a negative offset that references the current execution's stack. This offset is determined at compile time and stored in the function's prototype in the program. Furthermore, an upvalue may reference not only local variables but also upvalues from the parent function (a situation that arises when at least three functions are nested). Thus, the array of upvalue indices in the function prototype stores a pair of values: a tag indicating whether the value is a local stack variable or an upvalue from a parent function and the corresponding index (either the negative stack offset or the parent function's upvalue index).
-When the closure escapes from the original function with\
-`RETURN` instruction, inserted `CLOSE` instruction before the return, which causes, Actual upvalues are moved from stack to somewhere on the heap memory. This Upvalue may be referenced from multiple locations when using nested closures, and some form of garbage collection needed to free memory after it is no longer referred.
+For example, consider a scenario where the upvalue indices in the program are specified as `[upvalue(1), local(3)]`. In this case, the instruction `GETUPVALUE 6 1` indicates that the value located at index `3` from the upvalue list (referenced by
+`upvalue(1)`) should be retrieved from `R(-3)` relative to the base pointer, and the result should be stored in `R(6)`.
+
+When a closure escapes its original function context through the `RETURN` instruction, the inserted `CLOSE` instruction moves the active upvalues from the stack to heap memory. These upvalues may be referenced from multiple locations, particularly in cases involving nested closures. Thus, a garbage collection mechanism is required to free memory once these upvalues are no longer in use.
+
+In 's VM, since the paradigm is call-by-value and there is no reassignment expression, the `SETUPVALUE` instruction is omitted. If reassignment is allowed, open upvalues would need to be implemented as shared memory cells, as the values might be accessed by multiple closures that could trigger a `CLOSE` operation.

-## Compilation to the VM instructions
+## Compilation to the VM Instructions
-```rust{#lst:bytecodes_onepole float="" floatplacement="H" label="lst:bytecodes_onepole" caption="\\it Compiled VM instructions of one-pole filter example in Listing \\ref{lst:onepole}"}
+``` {#lst:bytecodes_onepole float="" floatplacement="H" label="lst:bytecodes_onepole" caption="\\it Compiled VM instructions of one-pole filter example in Listing \\ref{lst:onepole}"}
CONSTANTS:[1.0]
- fn onepole(x,g) state_size:1
- MOVECONST 2 0 // load 1.0
- MOVE 3 1 // load g
- SUBF 2 2 3 // 1.0 - g
- MOVE 3 0 // load x
- MULF 2 2 3 // x * (1.0-g)
- GETSTATE 3 // load self
- MOVE 4 1 // load g
- MULF 3 3 4 // self * g
- ADDF 2 2 3 // compute result
- GETSTATE 3 // prepare return value
- SETSTATE 2 // store to self
- RETURN 3 1
+ fn onepole(x,g) state_size:1
+ MOVECONST 2 0 // load 1.0
+ MOVE 3 1 // load g
+ SUBF 2 2 3 // 1.0 - g
+ MOVE 3 0 // load x
+ MULF 2 2 3 // x * (1.0-g)
+ GETSTATE 3 // load self
+ MOVE 4 1 // load g
+ MULF 3 3 4 // self * g
+ ADDF 2 2 3 // compute result
+ GETSTATE 3 // prepare return value
+ SETSTATE 2 // store to self
+ RETURN 3 1
```
-Listing [\[lst:bytecodes_onepole\]](#lst:bytecodes_onepole) shows an basic example when the *mimium* code in Listing [\[lst:onepole\]](#lst:onepole) is compiled into VM bytecode. When `self` is referenced, the value is obtained with the `GETSTATE` instruction, and the internal state is updated by storing the return value with the `SETSTATE` instruction before returning the value with `RETURN` from the function. Here, the actual return value is obtained by the second `GETSTATE` instruction in order to return the initial value of the internal state when time=0.
+Listing [\[lst:bytecodes_onepole\]](#lst:bytecodes_onepole){reference-type="ref" reference="lst:bytecodes_onepole"} shows a basic example of how the mimium code in Listing [\[lst:onepole\]](#lst:onepole){reference-type="ref" reference="lst:onepole"} is compiled into VM bytecode. When `self` is referenced, the value is retrieved using the `GETSTATE` instruction, and the internal state is updated by storing the return value with the `SETSTATE` instruction before returning it via the `RETURN` instruction. In this case, the actual return value is obtained using the second `GETSTATE` instruction, which ensures that the initial state value is returned at time = 0.
-For example, when a time counter is written as `| | {self + 1}`, it is the compiler's design choice whether the return value of time=0 should be 0 or 1 though the latter does not strictly follow the semantics E-FEED in Figure [\[fig:semantics\]](#fig:semantics). If the design is to return 1 when time = 0, the second `GETSTATE` instruction can be removed and the value for the `RETURN` instruction should be `R(2)`.
+For example, if a time counter is written as $feed x. x+1$, the decision on whether the return value at time = 0 should be 0 or 1 is left to the compiler design. Although returning 1 does not strictly follow the semantics of E-FEED in Figure [\[fig:semantics\]](#fig:semantics){reference-type="ref" reference="fig:semantics"}, if the compiler is designed to return 1 at time = 0, the second `GETSTATE` instruction can be omitted, and the value for the `RETURN` instruction should be `R(2)`.
-A more complex example code and its expected bytecode instructions are shown in Listing [\[lst:fbdelay\]](#lst:fbdelay) and Listing [\[lst:bytecodes_fbdelay\]](#lst:bytecodes_fbdelay). The codes define delay with a feedback as `fbdelay`, the other function `twodelay` uses two feedback delay with different parameters, and `dsp` finally uses two `twodelay` function.
+A more complex example, along with its expected bytecode instructions, is shown in Listings [\[lst:fbdelay\]](#lst:fbdelay){reference-type="ref" reference="lst:fbdelay"} and [\[lst:bytecodes_fbdelay\]](#lst:bytecodes_fbdelay){reference-type="ref" reference="lst:bytecodes_fbdelay"}. The code defines a delay with feedback as `fbdelay`, while another function, `twodelay`, uses two feedback delays with different parameters. Finally, `dsp` uses two `twodelay` functions.
-Each after the referring to `self` through `GETSTATE` instruction, or call to the other statefull function,\
-`SHIFTSTATE` instruction inserted to move the `StatePtr` forward to prepare the next non-closure function call. Before exits function, `StatePtr` is reset to the same position as that the current function context has begun by `SHIFTSTATE` (A sum of the operand for `SHIFTSTATE` in a function must be always 0).
+After each reference to `self` through the `GETSTATE` instruction or after calling another stateful function, the `SHIFTSTATE` instruction is inserted to advance the state storage position in preparation for the next non-closure function call. Before the function exits, the state position is reset to where it was at the beginning of the current function context using the `SHIFTSTATE` instruction. The total operand value for `SHIFTSTATE` within a function must always sum to 0. Figure [4](#fig:fbdelay_spos){reference-type="ref" reference="fig:fbdelay_spos"} illustrates how the state position shifts with the `SHIFTSTATE` operations during the execution of the `twodelay` function. The argument for the `SHIFTSTATE` operation is a word size (a number of 64 bit values) and the word size for delay is `maximum delay time + 3` since the read index, write index and the length of the ring buffer are added.
-```rust {#lst:fbdelay .Rust float="" floatplacement="H" label="lst:fbdelay" language="Rust" caption="\\it Example code that combines self and delay without closure call."}
+The state data can be stored as a flat array by representing the internal state as a relative position within the state storage, thereby simplifying compiler implementation; this avoids the need to generate a tree structure from the root, which was required in the previous implementation of mimium. This approach is similar to how upvalues simplify the compiler implementation by treating free variables as relative positions on the call stack.
+
+``` {#lst:fbdelay .Rust float="" floatplacement="H" label="lst:fbdelay" language="Rust" caption="\\it Example code that combines self and delay without closure call."}
fn fbdelay(x,fb,dtime){
- x + delay(1000,self,dtime)*fb
-}
-fn twodelay(x,dtime){
- fbdelay(x,dtime,0.7)
- +fbdelay(x,dtime*2,0.8)
-}
-fn dsp(x){
- twodelay(x,400)+twodelay(x,800)
-}
-```
-
-```rust {#lst:bytecodes_fbdelay float="" floatplacement="H" label="lst:bytecodes_fbdelay" caption="\\it Compiled VM instructions of one-pole filter example in Listing \\ref{lst:fbdelay}"}
-CONSTANTS:[0.7,2,0.8,400,800,0,1]
-fn fbdelay(x,fb,dtime) state_size:2
-MOVE 3 0
-GETSTATE 4
-SHIFTSTATE 1
-DELAY 4 2
-MOVE 5 1
-MULF 4 4 5
-ADDF 3 3 4
-SHIFTSTATE -1
-GETSTATE 4
-SETSTATE 3
-RETURN 4 1
-
-fn twodelay(x,dtime) state_size:4
-MOVECONST 2 5 //load "fbdelay" prototype
-MOVE 3 0
-MOVE 4 1
-MOVECONST 5 0 //load 0.7
-CALL 2 3 1
-SHIFTSTATE 2 //=state_size of fbdelay
-MOVECONST 3 5 //load "fbdelay" prototype
-MOVE 4 0
-MOVECONST 5 1 //load 2
-MULF 4 4 5
-MOVECONST 5 0 //load 0.7
-CALL 3 3 1
-ADDF 3 3 4
-SHIFTSTATE -2
-RETURN 3 1
-
-fn dsp (x)
-MOVECONST 1 6 //load "twodelay prototype"
-MOVE 2 0
-MOVECONST 3 3 //load 400
-CALL 1 2 1
-SHIFTSTATE 4 //=state_size of twodelay
-MOVECONST 2 6
-MOVE 2 3 //load "twodelay prototype"
-MOVE 3 0
-MOVECONST 3 4 //load 400
-CALL 2 2 1
-ADD 1 1 2
-SHIFTSTATE -4
-RETURN 1 1
-```
-
-# Discussion : Different behaviour depending on the position of let binding {#sec:discussion}
-
-Because *mimium* treats functions that have internal states which change over time, when higher-order functions are used, there is a counterintuitive behavior compared to general functional programming languages.
-
-An example is the higher-order function `filterbank`, which duplicates `filter` function `N` times parametrically, and adds them together. Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad) is an example of an incorrect code, and Listing [\[lst:filterbank_good\]](#lst:filterbank_good) is an example of the code that behave correctly. The difference between Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad) and Listing [\[lst:filterbank_good\]](#lst:filterbank_good)is that the recursive calls in the filterbank function are written directly in the inner function to be returned, and the recursive calls in the filterbank function are written with `let` binding out of the inner function[^2]. Similarly, in the `dsp` function that will be called by the audio driver in *mimium*, the difference is whether the filterbank function is executed inside `dsp` or bound with `let` once in the global context.
-
-In the case of normal functional language, if all the functions used in a composition do not contain destructive assignments, the calculation process will not change even if the variable bound by `let` is manually replaced with its term (beta reduction), as in the conversion from Listing [\[lst:filterbank_good\]](#lst:filterbank_good) to Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad).
-
-But in *mimium*, there are two major stages of evaluation, 0: the code is evaluated in the global environment (concretizing the signal processing graph) at first, and 1: the dsp function is repeatedly executed (actual signal processing) and the function may involve implicit internal state updates. Therefore, even though the code does not include destructive assignments, the recursive execution of the `filterbank` function is performed only once in Listing [\[lst:filterbank_good\]](#lst:filterbank_good) for the evaluation of the global environment, whereas in Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad), every sample the dsp function is executed, the recursive function is executed and a closure is generated. Since the initialization of the internal state in the closure is performed at the time of closure allocation, in the example of Listing[\[lst:filterbank_bad\]](#lst:filterbank_bad), the internal state of the closure after the evaluation of `filterbank` is reset at each time step.
-
-```rust {#lst:filterbank_bad .Rust float="" floatplacement="H" label="lst:filterbank_bad" language="Rust" caption="\\it Wrong example of the code that duplicate filter parametrically."}
-fn bandpass(x,freq){
- //...
-}
-fn filterbank(n,filter){
- if (n>0){
- |x,freq| filter(x,freq+n*100)
- + filterbank(n-1,filter)(x,freq)
- }else{
- 0
+ x + delay(1000,self,dtime)*fb
+ }
+ fn twodelay(x,dtime){
+ fbdelay(x,dtime,0.7)
+ +fbdelay(x,dtime*2,0.8)
+ }
+ fn dsp(x){
+ twodelay(x,400)+twodelay(x,800)
}
-}
-fn dsp(){ //called by audio driver.
- filterbank(3,bandpass)
-}
```
-```rust {#lst:filterbank_good .Rust float="" floatplacement="H" label="lst:filterbank_good" language="Rust" caption="\\it Corrected example of the code that duplicate filter parametrically."}
+``` {#lst:bytecodes_fbdelay float="" floatplacement="H" label="lst:bytecodes_fbdelay" caption="\\it Compiled VM instructions of feedback delay example in Listing \\ref{lst:fbdelay}"}
+CONSTANTS:[0.7,2,0.8,400,800,0,1]
+ fn fbdelay(x,fb,dtime) state_size:1004
+ MOVE 3 0 //load x
+ GETSTATE 4 //load self
+ SHIFTSTATE 1 //shift Spos
+ DELAY 4 4 2 //delay(_,_,_)
+ MOVE 5 1 // load fb
+ MULF 4 4 5 //delayed val *fb
+ ADDF 3 3 4 // x+
+ SHIFTSTATE -1 //reset SPos
+ GETSTATE 4 //prepare result
+ SETSTATE 3 //store to self
+ RETURN 4 1 //return previous self
+
+ fn twodelay(x,dtime) state_size:2008
+ MOVECONST 2 5 //load "fbdelay" prototype
+ MOVE 3 0
+ MOVE 4 1
+ MOVECONST 5 0 //load 0.7
+ CALL 2 3 1
+ SHIFTSTATE 1004 //1004=state_size of fbdelay
+ MOVECONST 3 5 //load "fbdelay" prototype
+ MOVE 4 0
+ MOVECONST 5 1 //load 2
+ MULF 4 4 5
+ MOVECONST 5 0 //load 0.7
+ CALL 3 3 1
+ ADDF 3 3 4
+ SHIFTSTATE -1004
+ RETURN 3 1
+
+ fn dsp (x)
+ MOVECONST 1 6 //load "twodelay" prototype
+ MOVE 2 0
+ MOVECONST 3 3 //load 400
+ CALL 1 2 1
+ SHIFTSTATE 2008
+ MOVECONST 2 6 //load "twodelay" prototype
+ MOVE 2 3
+ MOVE 3 0
+ MOVECONST 3 4 //load 400
+ CALL 2 2 1
+ ADD 1 1 2
+ SHIFTSTATE -2008
+ RETURN 1 1
+```
+
+![[]{#fig:fbdelay_spos label="fig:fbdelay_spos"}*Image of how the state position moves while executing `twodelay` function in Listing [\[lst:bytecodes_fbdelay\]](#lst:bytecodes_fbdelay){reference-type="ref" reference="lst:bytecodes_fbdelay"}.*](fbdelay_spos.pdf){#fig:fbdelay_spos width="0.7\\hsize"}
+
+Listing [\[lst:filterbank_good\]](#lst:filterbank_good){reference-type="ref" reference="lst:filterbank_good"} shows an example of a higher-order function
+`filterbank`, which takes another function `filter`---accepting an input and a frequency as arguments---duplicates `n` instances of `filter` and adds them together[^3].
+
+The previous mimium compiler was unable to compile code that took a function with an internal state as an argument because the entire tree of internal states had to be statically determined at compile time. However, the VM in can handle this dynamically. Listing [\[lst:bytecode_filterbank\]](#lst:bytecode_filterbank){reference-type="ref" reference="lst:bytecode_filterbank"} shows the translated VM instructions for this code. Recursive calls on the first line of `filterbank`, as well as calls to functions passed as arguments or obtained through upvalues (like `filter`), are executed using the `CALLCLS` instruction rather than the `CALL` instruction. The `GETSTATE` and `SETSTATE` instructions are not used in this function because the internal state storage is switched dynamically when the `CALLCLS` instruction is interpreted.
+
+``` {#lst:filterbank_good .Rust float="" floatplacement="H" label="lst:filterbank_good" language="Rust" caption="\\it Example code that duplicates filter parametrically using a recursive function and closure."}
+fn bandpass(x,freq){
+ //...
+ }
+ fn filterbank(n,filter){
+ let next = filterbank(n-1,filter)
+ if (n>0){
+ |x,freq| filter(x,freq+n*100)
+ + next(x,freq)
+ }else{
+ |x,freq| 0
+ }
+ }
+ let myfilter = filterbank(3,bandpass)
+ fn dsp(){
+ myfilter(x,1000)
+ }
+```
+
+``` {#lst:bytecode_filterbank float="" floatplacement="H" label="lst:bytecode_filterbank" caption="\\it Compiled VM instructions filterbank example in Listing \\ref{lst:filterbank_good}"}
+CONSTANTS[100,1,0,2]
+ fn inner_then(x,freq)
+ //upvalue:[local(4),local(3),local(2),local(1)]
+ GETUPVALUE 3 2 //load filter
+ MOVE 4 0
+ MOVE 5 1
+ GETUPVALUE 6 1 //load n
+ ADDD 5 5 6
+ MOVECONST 6 0
+ MULF 5 5 6
+ CALLCLS 3 2 1 //call filter
+ GETUPVALUE 4 4 //load next
+ MOVE 5 0
+ MOVE 6 1
+ CALLCLS 4 2 1 //call next
+ ADDF 3 3 4
+ RETURN 3 1
+
+ fn inner_else(x,freq)
+ MOVECONST 2 2
+ RETURN 2 1
+
+ fn filterbank(n,filter)
+ MOVECONST 2 1 //load itself
+ MOVE 3 0 //load n
+ MOVECONST 4 1 //load 1
+ SUBF 3 3 4
+ MOVECONST 4 2 //load inner_then
+ CALLCLS 2 2 1 //recursive call
+ MOVE 3 0
+ MOVECONST 4 2 //load 0
+ SUBF 3 3 4
+ JMPIFNEG 3 2
+ MOVECONST 3 2 //load inner_then
+ CLOSURE 3 3 //load inner_lambda
+ JMP 2
+ MOVECONST 3 3 //load inner_else
+ CLOSURE 3 3
+ CLOSE 2
+ RETURN 3 1
+```
+
+# Discussion {#sec:discussion}
+
+As demonstrated in the example of the filterbank, in , a signal graph can be parametrically generated during the evaluation of the global context, whereas Faust uses a term-rewriting macro and Kronos employs type-level computation, as shown in Table [1](#tab:comparison){reference-type="ref" reference="tab:comparison"}.
+
+The ability to describe both the generation of parametric signal processing and its execution content within single semantics makes it easier for novice users to understand the mechanics of the language. In addition, unified semantics may simplify runtime interoperability with other general-purpose languages.
+
+However, there is a drawback: unified semantics can cause to deviate from the behavior typically expected in standard lambda calculus.
+
+::: {#tab:comparison}
+ 1-3 Parametric Signal Graph Actual DSP
+ ---------------- ------------------------- ------------------
+ 1-3 Faust Term Rewriting Macro BDA
+ 1-3 Kronos Type-level Computation Value Evaluation
+ 1-3
+ Global Context
+ `dsp` Function
+ 1-3
+
+ : *Comparison of the way of signal graph generation and actual signal processing between Faust, Kronos and .*
+:::
+
+## Different Behaviour Depending on the Location of Let Binding {#sec:letbinding}
+
+By using functions with internal states that change over time in mimium, there is counterintuitive behavior when higher-order functions are used compared to general functional programming languages.
+
+Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad){reference-type="ref" reference="lst:filterbank_bad"} presents an example of incorrect code that is slightly modified from the filterbank example in Listing [\[lst:filterbank_good\]](#lst:filterbank_good){reference-type="ref" reference="lst:filterbank_good"}. The main difference between Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad){reference-type="ref" reference="lst:filterbank_bad"} and Listing [\[lst:filterbank_good\]](#lst:filterbank_good){reference-type="ref" reference="lst:filterbank_good"} is whether the recursive calls in the `filterbank` function are written directly or bound using a `let` expression outside the inner function. Similarly, in the `dsp` function, which is called by the audio driver in mimium, the difference lies in whether the `filterbank` function is executed within `dsp` or bound with `let` once in the global context.
+
+In a typical functional programming language, if none of the functions in the composition involve destructive assignments, the calculation process remains unchanged even if the variable bound by `let` is replaced with its term (via beta reduction), as seen in the transformation from Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad){reference-type="ref" reference="lst:filterbank_bad"} to Listing [\[lst:filterbank_good\]](#lst:filterbank_good){reference-type="ref" reference="lst:filterbank_good"}.
+
+However, in mimium, there are two distinct stages of evaluation. 0: The code is first evaluated in a global environment (where the signal-processing graph is concretized). 1: The `dsp` function is executed repeatedly (handling the actual signal processing) and may involve implicit updates to the internal states.
+
+Although the code contains no destructive assignments, the recursive execution of the `filterbank` function occurs only once in Listing [\[lst:filterbank_good\]](#lst:filterbank_good){reference-type="ref" reference="lst:filterbank_good"} during the global environment evaluation. Conversely, in Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad){reference-type="ref" reference="lst:filterbank_bad"}, the recursive function is executed, and a closure is generated each time the `dsp` function runs on every sample. Because the internal state of the closure is initialized at the time of closure allocation, in the example of Listing [\[lst:filterbank_bad\]](#lst:filterbank_bad){reference-type="ref" reference="lst:filterbank_bad"}, the internal state of the closure is reset at each time step, following the evaluation of `filterbank`.
+
+``` {#lst:filterbank_bad .Rust float="" floatplacement="H" label="lst:filterbank_bad" language="Rust" caption="\\it Wrong example of the code that duplicate filter parametrically."}
fn filterbank(n,filter){
- let next = filterbank(n-1,filter)
- if (n>0){
- |x,freq| filter(x,freq+n*100)
- + next(x,freq)
- }else{
- 0
- }
-}
-let myfilter = filterbank(3,bandpass)
-fn dsp(){
- myfilter(x,1000)
-}
+ if (n>0){
+ |x,freq| filter(x,freq+n*100)
+ + filterbank(n-1,filter)(x,freq)
+ }else{
+ |x,freq| 0
+ }
+ }
+ fn dsp(){
+ filterbank(3,bandpass)(x,1000)
+ }
```
-```rust {#lst:filterbank_multi .Rust float="" floatplacement="H" label="lst:filterbank_multi" language="Rust" caption="\\it Example of filterbank function using multi-stage computation in a future specification of mimium."}
+``` {#lst:filterbank_multi .Rust float="" floatplacement="H" label="lst:filterbank_multi" language="Rust" caption="\\it Example of filterbank function using multi-stage computation in a future specification of mimium."}
fn filterbank(n,filter){
- .< if (n>0){
- |x,freq| filter(x,freq+n*100)
- + ~filterbank(n-1,filter)(x,freq)
- }else{
- 0
- } >.
- }
- fn dsp(){
- ~filterbank(3,bandpass) (x,1000)
- }
+ .< if (n>0){
+ |x,freq| filter(x,freq+n*100)
+ + ~filterbank(n-1,filter)(x,freq)
+ }else{
+ |x,freq| 0
+ } >.
+ }
+ fn dsp(){
+ ~filterbank(3,bandpass)(x,1000)
+ }
```
-This means that the major compiler optimization techniques such as the constant folding and the function inlining can not simply be appropriated. Those optimizations should be done after the evaluation of a global context and before evaluating `dsp` function.
+This implies that major compiler optimization techniques, such as constant folding and function inlining, cannot be directly applied to mimium. These optimizations must be performed after global context evaluation and before the evaluation of the `dsp` function.
-To solve this situation, introducing distinction whether the term should be used in global context evaluation(Stage 0) and in the actual signal processing(stage 1) in type system. This can be realized with Multi-Stage Computation[@Taha1997]. Listing [\[lst:filterbank_multi\]](#lst:filterbank_multi) is the example of `filterbank` code using BER MetaOCaml's syntaxes `..` which will generate evaluated program used in a next stage, and `~term` which embed terms evaluated at the previous stage[@kiselyov2014a].
+To address this issue, it is necessary to introduce a distinction in the type system to indicate whether a term should be used during global context evaluation (stage 0) or actual signal processing (stage 1). This can be achieved with Multi-Stage Computation [@Taha1997]. Listing [\[lst:filterbank_multi\]](#lst:filterbank_multi){reference-type="ref" reference="lst:filterbank_multi"} provides an example of the `filterbank` code using BER MetaOCaml's syntax: `..`, which generates a program to be used in the next stage, and `~term`, which embeds the terms evaluated in the previous stage [@kiselyov2014a].
-`filterbank` function is evaluated in stage 0 while embedding itself by using `~`.
+The `filterbank` function is evaluated in stage 0 while embedding itself with `~`. In contrast to Faust and Kronos, this multi-stage computation code retains the same semantics for both the generation of the signal processing graph and the execution of signal processing.
-This multi-stage computation code has a same semantics in a generative signal graph generation and execution of the signal processing, in contrast to that Faust have 2 different semantics of the term rewriting macro and BDA.
+## A Possibility of the Foreign Stateful Function Call
-# Conclusion
+The closure data structure in combines functions with the internal states, as shown in Figure 3. The fact that `filterbank` samples do not require special handling for internal states means that external signal processors (Unit Generators: UGens), such as oscillators and filters written in C or C++, can be called from mimium, just like normal closure calls. Additionally, it is possible to parameterize, duplicate, and combine external UGens[^4]. This capability is difficult to implement in Faust and similar languages but is easily achievable in the paradigm.
-[\[sec:conclusion\]](#sec:conclusion)
+However, mimium currently uses sample-by-sample processing and cannot handle buffer-by-buffer value passing. Because most native unit generators process data on a buffer-by-buffer basis, there are few practical cases where external UGens are currently used. Nonetheless, in , only $feed$ terms require sample-by-sample processing. Therefore, it is possible to differentiate the functions that can process only one sample at a time from those that can process concurrently at the type level. As the multi-rate specification is being considered in Faust [@jouvelotDependentVectorTypes2011], it may be possible to facilitate buffer-based processing between an external Unit Generator by having the compiler automatically determine the parts that can be processed buffer-by-buffer.
-This paper proposed $\lambda_{mmm}$, an intermediate representation for the programming languages for music and signal processing with the virtual machine and instruction set to run it. $\lambda_{mmm}$enables to describe generative signal graph and its contents in a single syntax and semantics. However, user have to be responsible to write codes that does not create escapable closures during the dsp execution and this problem would be difficult to understand by novice users.
+# Conclusion {#sec:conclusion}
+
+This paper proposed $\lambda_{mmm}$, an intermediate representation for programming languages for music and signal processing, along with a virtual machine and an instruction set to execute it. $\lambda_{mmm}$ enables the description of generative signal graphs and their contents within a unified syntax and semantics. However, users are responsible for ensuring that their code does not create escapable closures during the iterative execution of a DSP, which can be challenging for novice users to grasp.
+
+In this paper, the translation of terms into VM instructions was illustrated by showing examples of code and the corresponding expected bytecode alongside pseudocode to describe the behavior of the VM. More formal semantics and a detailed translation process should be considered, particularly with the introduction of multi-stage computation.
+
+I hope that this research will contribute to more general representations of music and sound on digital computers and foster deeper connections between the theory of languages for music and the broader field of programming language theory.
# Acknowledgments
-This work was supported by JSPS KAKENHI (Grant No.\
-JP19K21615). Also great thanks for many anonymous reviewers.
+This study was supported by JSPS KAKENHI (Grant No.
+JP19K21615). I would also like to thank the many anonymous reviewers.
-[^1]: Reason for this is that it is easy to implemented on `enum` data structure on Rust, a host language of the latest *mimium* compiler. Operands bitwidth and alignment may be changed in the future.
+[^1]: The newer version of mimium compiler and VM based on the model presented in this paper is on the GitHub.
-[^2]: In the previous specification of *mimium* in [@matsuura2021a], the binding of new variable and destructive assignment were the same syntax(`x = a`) but the syntax for the variable binding has changed to use `let` keyword.
+[^2]: In the actual implementation, instructions such as `MOVE` include an additional operand to specify the word size of values, particularly for handling aggregate types like tuples.
-::: {#refs}
-:::
\ No newline at end of file
+[^3]: In the previous specification of mimium [@matsuura2021a], the syntax for the variable binding and destructive assignment was the same (`x = a`). However, in the current syntax, variable binding uses the `let` keyword.
+
+[^4]: In fact, in the actual implementation of mimium, an interoperation between VM and audio driver is realized by passing and calling Rust's closure.