On the cost semantics for spreadsheets with sheet-defined functions

We give a simple operational cost semantics for evaluation of spreadsheet formulas and for full and minimal recalculation. We also present a semantics which caters for computing with unknown data values. This may be used to give an approximation of the cost when input data is not yet provided. This semantics is a rudimentary big-step trace-based abstract interpretation based upon the cost semantics. Together, the semantic presentations form the formal foundations for various cost calculations implemented in the Funcalc spreadsheet platform. This can be used in cost estimation tools, e.g. to estimate which formulas in a spreadsheet are the most expensive, or to schedule parallel recalculation of a spreadsheet. In future work, further analyzes and verification tools can be built upon the formal semantics to reduce the large number of errors that commonly occur in spreadsheets.


Introduction
Spreadsheets are used by millions of people, ranging from pupils doing their school hand-ins to complex financial, medical or scientific computations. In 2017 it was estimated that there were 13-25 million spreadsheet developers worldwide [1], i.e. people developing complex computations using spreadsheets. Yet despite their widespread use it is almost impossible to predict or analyze the computational cost of spreadsheet computations. Some complex spreadsheets may take a long time to recalculate.
For instance, the building-design and ground-water benchmarks developed in connection with a study of parallelization of the LibreOffice spreadsheet program on AMD GPUs [2], have about one million data cells and 108,332 and 126,404 formula cells respectively and updates may take a long time. Other spreadsheets, such as energy-market with 534,507 formula cells, may take even longer.
As noted already by Mani Chandy in his 1985 keynote Concurrent programming for the masses, spreadsheets have a programming model that should be much easier to parallelize than traditional programming languages [3]. However, only sporadic efforts have been made in this area in the last 30 years [2,[4][5][6].
functions bring a natural abstraction mechanism to the world of spreadsheets. Funcalc also supports the notion of array formulas, as found in popular spreadsheet implementations such as Excel and OpenOffice Calc. Sheet-defined functions can be higher-order functions. Together with first class array formulas and a few simple built-in functions, such as map, reduce and fold, the expression language of Funcalc is an expressive, yet pure, higher-order functional programming language.
Inspired by Gomez et al. [12] and Rosendahl [13] we give a simple cost semantics for evaluation of a spreadsheet formula and for full and minimal recalculation of a spreadsheet. The cost semantics is a straightforward extension of our big-step semantics for spreadsheets [10]. However, the cost semantics differ in subtle ways from the original bigstep semantics. We discuss the subtleties, especially their implications for implementations.
Analyzing or calculating cost or complexity of (higher-order) functional programming languages goes back to [14][15][16][17]. More recent work based on language semantics extended with some notion of cost have been presented in [18][19][20][21][22][23]. Both [24,25] introduce big-step cost semantics for functional programming languages and relate the lazy, respectively, the eager, evaluation strategies to implementations. A bigstep cost semantics for a subset of the ML programming language is presented in [26], very closely akin to the cost semantics presented in this paper. The main difference between [24][25][26] and the work presented in this paper, apart from the application domain of spreadsheets, is the introduction of non-determinism in our cost semantics to allow for different implementations, e.g. allowing for evaluating arguments to functions in a left to right order or in parallel.
Both Gomez et al. and Rosendahl worked on cost translations for higher-order functional languages [12,13] which cater for computing with unknown data values. Adding such unknown values allows for a rudimentary abstract interpretation of programs which in many cases can provide a rather precise approximation of the actual cost of the computation. To provide a semantic foundation for calculation with unknown data values, we follow the ideas presented by Schmidt [27] and provide a big-step trace-based abstract interpretation for the cost semantics.
The cost semantics form the formal foundations for various cost calculations implemented in the Funcalc spreadsheet platform.
The rest of this paper is organized as follows: Section 2 presents the cost semantics, with 2.1 giving a cost semantics for simple spreadsheet formulas extending the semantics described in [7]. In Section 3 we give a cost semantics for Funcalc extended spreadsheet expressions and in Section 4 we give cost semantics for intrinsic functions. The extended evaluation semantics for Funcalc is further augmented to compute with unknown values in Section 5; this is a first step towards an approximate cost analysis based on abstract interpretation. Implementations for the concrete and abstract cost semantics are presented in Section 6. In Section 7, we present results for an initial implementation of a cost evaluator built upon the rules of our cost semantics. Conclusions and future work are presented in Section 8.

Cost semantics
In this section, we present a cost semantics for spreadsheet expressions which in addition to a computed value of the expression describes the possible cost of computing it. More precisely, the semantics describes the work, i.e. uni-processor cost [8], of the computation. In a parallel implementation, some of that work may be performed in parallel.
First, we give an operational cost semantics for simple spreadsheet expressions based on the evaluation semantics presented in [7] and extended in [10]. Then Section 3 extends the cost semantics to cover array formulas and sheet-defined functions.
In all cases the amount of work is described by a non-negative integer in 0 representing some notion of computation step, for instance the number of rule applications, plus some measure of the  This notion of work can reasonably be assumed to be within a constant factor of the actual number of nanoseconds required to evaluate an expression.

Cost semantics for simple formulas
For clarity of presentation, we start by giving a cost semantics for simple spreadsheet formulas as described in [7]. The simplified formulas used in this section are described in Fig. 1. One simplification is to represent a constant cell n by a constant formula =n, although most spreadsheet programs would distinguish them. Another simplification is to leave out cell area expressions 1 ∶ 2 ; these will be introduced in Section 3.1.
Volatile functions, such as RAND in Fig. 1, are special functions that are unconditionally evaluated when recalculating. For instance, the function RAND produces random numbers and must be volatile to ensure that every recalculation produces a new random number even if the cell containing the volatile function is otherwise unchanged.
To describe the evaluation of formulas, we use the semantic sets and functions defined in Fig. 2. These are sometimes called semantic domains, but here they are ordinary sets and partial functions. For instance, = + is the set of values, where a value is either a (finite, non-NaN) IEEE 854 binary floating-point number such as 0.42 in set or an error such as #DIV/0! in set .

The set
contains cell addresses such as B2. For presentational simplicity, some additional error values (such as #NAME!) and additional kinds of values (such as strings), found in realistic spreadsheet programs, have been left out. They are easily added to the semantics studied here but otherwise provide no additional semantic insight. We use a map ∶ → so that when ∈ is a cell address, ( ) is the formula in cell . If cell is blank, then ( ) is undefined. The domain of is the set of cell addresses that have a formula i.e. the set of non-blank cells function is not affected by recalculation, only by editing the sheet.
The result of a recalculation is modeled by function ∶ → , where ( ) is the computed value in cell . The function gets updated by each recalculation (see Section 2.3).
It is quite straightforward to extend the evaluation semantics rules in [7,10] to the new cost semantics rules given in Fig. 3.
The evaluation judgment ⊢ ⇓ gets extended to ⊢ ⇓ , where is a computed value of the expression and is the cost of computing that value. This judgment states that when describes the calculated values of all cells, then formula may evaluate to value at computational cost . As in [10], the semantics is nondeterministic (''may'') in the sense that the evaluation of an expression could produce many different values at many different costs . The cost of evaluating is given under the assumption that all referred-to cells are already computed.
The formula evaluation rules in Fig. 3 are explained as follows: Rule (c1) says that evaluating a number constant n requires 1 computation step, and similarly for cell references by rules (c2b) and (c2v). Rule (c3e) says that if 1 may evaluate to error 1 in 1 computation steps, then ( 1 , 2 , 3 ) may evaluate to error 1 in 1 + 1 computation steps.
Rule (c3f) says that if 1 may evaluate to the non-error number 0.0 in 1 computation steps and the ''false branch'' 3 may evaluate to in 3 computation steps, then ( 1 , 2 , 3 ) may evaluate to value in 1 + 1 + 3 computation steps.
Rule (c3t) is similar to rule (c3f) for when 1 may evaluate to some non-error non-zero number 1 in 1 computation steps. Note that although in numeric software it is bad practice to compare floatingpoint numbers for equality, an IEEE floating-point number either is or is not equal to zero, so semantically the comparison 1 ≠ 0.0 is unproblematic; and also this rule reflects spreadsheet reality.
Rule (c4) says that function call () may evaluate to any (nonerror) number greater than or equal to zero and less than one, in one computation step.
Rule (c5e) says that an implementation may choose to evaluate just a subset { | ∈ } of the arguments when some with ∈ evaluates to an error , and then let be the result of the function call. Also, it says that the total cost of this is the cost ∑ ∈ of evaluating that subset of arguments, plus one. The rationale for this is discussed in Section 2.2.
Rule (c5v) says that if each argument may evaluate to a nonerror value in computation steps and applying the actual function to argument values ( 1 , … , ) produces value at a cost of ( , 1 , … , ) computation steps, then the call ( 1 , … , ) may evaluate to value using a total of 1 + ∑ =1, describes the cost of applying function to argument values ( 1 , … , ). For instance, one would expect (+, 1 , 2 ) = 1 since the cost of addition is independent of the actual numbers added. By contrast, for functions on array values one would expect the cost to depend on the argument array size; for instance, ( , 1 ) = ⋅ ℎ + 1 when array value 1 has columns and ℎ rows. This will be defined in Section 4 when we discuss Funcalc's intrinsic functions.
Making each cost rule add 1 to the cost incurred by subexpression evaluations may appear very simplistic. It means that the cost semantics essentially counts the number of rule applications and ensures that costs increase monotonically. A more realistic cost semantics might replace each occurrence of ''1'' with a suitable constant indicating a number of nanoseconds for the operation, such as 1 for evaluating a constant, 8 for evaluating a cell reference, 40 for a call to RAND, and similar.
However, if we are interested in cost up to a constant factor, counting the number of rule applications works just as well, and avoids some notational clutter. Also, the real time cost of something as simple as a cell reference may vary from 1 ns to 80 ns depending on whether the relevant data is already in the CPU hardware cache or not.

Rationale for cost of an error argument
While most of the cost semantics rules in Fig. 3 are obvious extensions of the evaluation rules presented in [10], this is not the case for rule (c5e) for unsuccessful function call evaluation. Here we explain why.
It is possible to imagine a cost rule (c5bad) like this: This rule says that if some argument may evaluate to an error using computation steps, then the call ( 1 , … , ) to a function F may evaluate to error in 1 + computation steps. However, this cost is unrealistically low: a conforming implementation would have to correctly guess which (if any) argument expression can evaluate to an error, and then evaluate only that expression. Such an implementation would seem implausibly clever. A more realistic rule might stipulate instead that the cost is (at least) the sum of the costs of evaluating all argument expressions. This corresponds to implementations that would evaluate all arguments before checking whether any of them evaluates to an error. However, this is needlessly pessimistic since an implementation may stop evaluating arguments once one of them evaluates to an error.
Another realistic cost rule might correspond to implementations that evaluate argument expressions 1 , 2 , … from left to right until one of them (if any) evaluates to an error. However, this restricts the possible implementations and would preclude or complicate parallel evaluation of arguments.
Instead we propose rule (c5e) in Fig. 3 which corresponds to implementations that may evaluate the argument expressions in any order (or in parallel) but may avoid evaluating all of them in case one evaluates to an error. As shown in the rule this corresponds to choosing a subset ⊆ {1, … , } of the argument indices and evaluating only the for which ∈ , to values at costs , where one of the is an error, and then stating that the total cost of the call is the sum ∑ ∈ of the costs of the arguments actually evaluated, plus one.
Since the set may be chosen in many ways, this rule introduces nondeterminism in the evaluation cost, in addition to nondeterminism in the computed value. Note also that rule (c5e) encompasses all three alternative rules discussed above, by choosing = { } as the singleton set for which is an error (using unrealistically perfect foresight), or = {1, … , } to evaluate all arguments, or = {1, … , } as the least prefix of argument indexes for which is an error.

Cost of simple recalculation and consistency
Sections 2.1 and 2.2 above gave evaluation-and-cost rules for evaluation of individual spreadsheet formulas. How do we describe the cost of a full recalculation or minimal recalculation in terms of these? First, we introduce a cost environment ∶ → 0 such that ( ) is the cost of evaluating the formula at cell address . Using this cost environment, we can now express the cost of a full recalculation of a spreadsheet described by and as the cost of evaluating the formula of every non-blank cell once: The purpose of a full recalculation is to compute a consistent spreadsheet: one in which the computed value of each non-blank cell agrees with its formula. This is formalized in Fig. 4.
Requirement (1) says that a recalculation must find a value ( ), possibly an error, as well as a cost ( ), for every non-blank cell .  Requirement (2) says that the computed value ( ) and cost ( ) must agree with the cell's formula ( ) for every non-blank cell , thus asserting that the spreadsheet's cell values as described by are consistent with each other.
To express the cost of a minimal recalculation initiated by editing a single cell 0 in a consistent spreadsheet represented by and , we need the set ( 0 ) of cells that must be recalculated after cell 0 has been edited. This set is defined in terms of the ''supports'' or ''precedent'' relation, where cell is said to support (or be a precedent of) cell if directly depends on , by 's formula containing a reference to . Now ( 0 ) is simply the transitive closure, under the ''supports'' relation, of the set containing cell 0 and every cell whose formula is volatile. Since ( 0 ) is defined via the ''supports'' relation it may be an overapproximation of the set of cells that really need to be evaluated in a minimal recalculation. Namely, if cell supports cell but a recalculation of happens not to change its value, cell might not really need to be recalculated, but ( ) would nevertheless contain . The consistency requirements following a minimal recalculation, shown in Fig. 5, refine those for full recalculation. There must be consistent spreadsheet values before the minimal recalculation; we again impose requirements (1) and (2) on the spreadsheet values ′ and costs ′ after the minimal recalculation. In addition, we require (3) that all cells not recalculated retain their values.
With these definitions, the total cost of a minimal recalculation after a change to cell 0 is the sum of the costs of evaluating the cells in ( 0 ): Note also that the consistency requirements and cost for full and minimal recalculation intentionally do not specify how recalculation actually proceeds, but specify only the requirements that must hold for each cell after recalculation.

Cost semantics for extended formulas
In this section we extend the cost semantics to cover array formulas and sheet-defined functions.

Extended expressions and semantic sets
The simple spreadsheet cost semantics from Section 2.1 must be expanded in two orthogonal directions: to account for array formulas and to account for sheet-defined functions. This requires extension to the formula expression language, shown in Fig. 6, and to the set of values and semantic maps, shown in Fig. 7.
A cell area reference 1 ∶ 2 refers to a block of cells spanned by the two opposing ''corner'' cells 1 and 2 . In Funcalc, a cell area reference can refer to an ordinary sheet only, not to a function sheet.
An array formula is here modeled as an underlying formula which is itself just an expression, expected to evaluate to an array  bound arguments in any argument position, using the NA function, which returns the #NA ''not available'' error, as a placeholder for late-bound arguments, as in =LOG(NA(), 2). One could model this behavior by also recording the argument position along with the value, but for simplicity we use a prefix here, as modeling the full behavior leads to a more complicated semantics without significant additional insight.
A closure is created by calling the CLOSURE built-in with a sheetdefined function and giving it values for some or all of its arguments. A partially applied closure 0 may be given further arguments, as in currying, also using CLOSURE. An APPLY call of a closure 0 must provide all the remaining arguments, where + = ( ), and will execute the underlying sheet-defined function.
The cost semantics for Funcalc extended spreadsheet formulas is given by judgments of the form , ⊢ ⇓ , which say that when describes cell values and describes array expression values, expression may evaluate to value at a cost of computation steps. The rules defining these judgments are given in Fig. 8.
The extended cost semantics rules in Fig. 8 draw on the simple cost semantics rules in Fig. 3.
Rules (g1) through (g5v) are very similar to the simple cost semantics rules (c1) through (c5v). This includes the somewhat complicated case (g5e) of a function argument evaluating to an error, explained in Section 2.2.
Rule (g6) states that the cost of evaluating a sheet cell area expression that produces an array value of columns and ℎ rows is ⋅ ℎ, the number of components in the resulting array value, plus one. The notation [ + , + ] denotes indexing into the sheet using and as offsets from the top left cell ( , ) of the cell area.
Rule (g7) states that the cost of evaluating a cell that is part of an array formula is 1. This is because we require the array formula's shared underlying array expression to be evaluated at most once in a recalculation, so that evaluating the cell is just a matter of indexing into the resulting array.
Rule (g8) states that the cost of calling a sheet-defined function is the cost of evaluating all arguments, plus the cost of evaluating the function body, plus one. We use a helper function def that returns the addresses of the output cell out, input cells [ 1 , … , ], and intermediate cells cells that compute intermediate values in the function. Each call, also each recursive call, has its own fresh ′ and ′ environments that are both ephemeral: there is no way to refer to a function sheet cell value after the function has returned. Hence these environments are similar to a stack frame in ordinary programming language implementation. The cost of evaluating the function body is the sum of the costs of evaluating the cells used to define the function, as described by the cost environment ′ . Here, Rule (g9) states that the cost of creating a closure is the cost of evaluating the given arguments, plus one.
Rule (g10) states that the cost of partially applying a closure is the cost of evaluating the first argument 0 to a function value with earlybound arguments, plus the cost of evaluating the given arguments to the call to CLOSURE, plus one. This may evaluate to a new function value with + arguments.
Rule (g11) states that the cost to call a closure is the cost of evaluating the closure expression, plus the cost of evaluating the remaining arguments, plus the cost of evaluating the called function's body, plus one. Similar to rule (g8), ( ′ ) must equal ( ′ ) ⧵ { 1 , … , + }, and Section 3.2 discusses how to choose ( ′ ) and hence ( ′ ).

The cost of calling a sheet-defined function
The rules (g8) and (g11) for calling a sheet-defined function leave unspecified the set ( ′ ) of the function's cells that should be evaluated, and hence the set , } whose evaluation costs should be included in the call cost.
The set ( ′ ) may contain all the function's cells, but it suffices to include only those cells actually needed to compute the value of the output cell . For an ordinary semantics this distinction is less important, since it does not affect the result ′ ( ) of the function call, assuming that evaluation terminates. However, for the cost semantics the distinction is crucial. Obviously, evaluating cells that are not needed, and hence including them in ( ′ ) and in ( ′ ), affects the cost ∑ ∈ ( ′ ) ′ ( ) of the computation. If the value of a cell is needed, directly or indirectly, to compute the value of the output cell , then must be in ( ′ ). Conversely, a cell whose value is not needed by the output cell should not be  If input B2 ≥ 1, the condition in output cell B5 is always true and the function evaluates to B3 without having to evaluate B4; and if B2 ≤ 0, the condition in B5 is always false, and cell B4 must be evaluated to produce the result of the function.
When 0 < B2 < 1, the value of RAND() determines whether cell B4 really needs to be evaluated. A reasonable cost semantics should allow for leaving out the cost of evaluating cell B4 when its value is not needed. On the other hand, it should also allow for adding in that cost, so as to correctly describe an implementation that speculatively evaluates B4 although its value may not be needed. For instance, an implementation may evaluate B4 to exploit available parallel computation resources, or simply because the cost of unconditionally evaluating B4 is smaller than the cost of determining whether its value is needed (and then performing the relevant conditional jumps, synchronization, or the like).
Thus in the semantics there should be some freedom in choosing ( ′ ) and hence ( ′ ) and hence the total cost of the function call. The choice should be subject to a consistency requirement: if cell ∈ ( ′ ) and the value of depends on cell , then ∈ ( ′ ) too.
How can we describe more formally that the value of a cell is needed to compute the output cell and hence the function's return value? In the Funcalc implementation, so-called evaluation conditions [7,Chapter 9] are used to control which cells must be evaluated. However, that is a particular implementation mechanism and should not be part of the cost semantics specification.
Hence we propose to specify the consistency requirement as follows. Consider an application of rule (g8) and all the inference trees that prove the judgments in the last row of premises. The domains ( ′ ) and hence Now the consistency requirement says that for each function-sheet cell reference ∈ encountered while building that inference tree, it is the case that ∈ ( ′ ). In other words, any (non-input) Fig. 10. A sheet-defined function EX such that EX( ,1) returns a random sample (1, 2, . . . ) from the geometric distribution with parameter . The function definition is similar to FCT in Fig. 9, but cell B4 contains a recursive call to EX itself, so now it is essential that cell B4 does not get evaluated unconditionally. By eventually evaluating B4 only when it is needed, we can achieve that a call EX( , ) terminates if and only if > 0.  cell referred to during the evaluation of the sheet-defined function must have a value, meaning ∈ ( ′ ), so that cell lookup succeeds for a cell reference inside a sheet-defined function. Also, the cost of that computation must be accounted for, meaning ∈ ( ′ ). Note that this consistency requirement is general enough to allow for speculative computation of unneeded cells, so long as this does not lead to an attempt to build an infinite inference tree, representing nonterminating recursion.
To illustrate the subtlety of the choice of whether to evaluate an unneeded cell, consider function EX in Fig. 10. This is a slight variant of FCT, where the decisive difference is that the trivial formula in B4 has been replaced with a recursive call =EX(B2, B3+1), so that now it is essential both for termination and correct cost accounting that B4 is evaluated only when needed.

Cost semantics for function sheets
A cost semantics for sheet-defined functions on function sheets can be given by rules defining judgments of the form , ⊢ ⇓ , . Such judgments are referred to in rules (g8) and (g11). Because the rules are simple extensions of the cost semantics in Fig. 8, we give only one of these rules here, in Fig. 11.

Cost of extended recalculation
The cost of recalculation for Funcalc extended formulas must account for array formulas and for sheet-defined functions.
The cost of evaluating the array expression underlying an array formula is defined as for any other expression. We use the environment also to record this cost as ( ), so its type is now ∶ + → 0 . The consistency requirements for a cost semantics accounting also for array formulas are shown in Fig. 12.
The total cost of a full recalculation therefore is the sum of computing the formula in every cell, plus the cost of computing the array expression underlying every array formula: We extend the ( 0 ) set to also include array expressions that need to be recalculated (in addition to non-array expression cells that also need to be recalculated), so now ( 0 ) ⊆ + . Hence the cost of a minimal recalculation can be expressed as before: where ′ is a cost environment determined in a similar manner as in Section 2.3.

Rules for intrinsic functions
In this section, we extend the operational cost semantics from Section 3 by expanding the function application rule (g5v) for a meaningful subset of intrinsic functions in Funcalc. Recall rule (g5v), repeated below for convenience.
By ''meaningful subset'' we mean that it is not sensible or interesting to give rules for some of the intrinsic functions. For example, EXTERN returns the result of a call to an external library. While the returned value can be (and is) given by a plain C# object type, its cost is undefined. The call may perform any operation from querying a database to initiating some long-running, unknown computation that we have insufficient knowledge to approximate. Alternatively, we could give meaningful rules for some common uses for EXTERN such as the methods in the .NET libraries, but we forgo this here. In our presentation, we focus only on ordinary, interpreted sheets, as the rules for function sheets are mostly analogous. As a starting point, consider the rule for the SIN function that computes the sine of its input value.
The rule states that if the expression may evaluate to a number at cost then the function application expression SIN(e) may evaluate to the actual function application ( ) at total cost 1 + . Similar rules can be given for COS and TAN.
We introduce a few conventions for array values that must be borne in mind when reading the semantic rules in the following sections. First, we use a more compact notation for array values: To refer to an entire column or row of an array value, we use notation such as ]] refers to the third row.
We define concatenation operators for array values and . The horizontal concatenation ∶ is the array consisting of 's columns on the left followed by 's columns on the right; here and must have the same number of rows. The vertical concatenation ; is the array consisting of 's rows on the top followed by 's rows below; here and must have the same number of columns. These operators are associative and have suitable zero-column or zero-row arrays as units. The concatenation operators do not produce nested array values: concatenating two array values creates a single array value with the elements from both.
Additionally, we can construct an array value from a set of values that may produce nested array values: . Each value of the new array value is exactly as given so if 2 is an array value itself, it will be nested inside of the constructed array value instead of its values being concatenated. In general, we mostly omit the ''error rules'' dealing with cases where a function argument evaluates to an error value, and ask the reader to imagine analogs of rule (g5e) in Section 3. For most functions, we assume all arguments are non-error values and that we evaluate them in some order according to a desired implementation. For full details of all functions available in Funcalc, we refer the reader to [7]. First-order intrinsic functions are given in Section 4.1, higher-order functions are given in Section 4.2.

Rules for first-order intrinsic functions
The rules for Funcalc's first-order intrinsic functions are given in Fig. 13 in the context of ordinary sheets.
Rule (now) has one premise stating that if the result may evaluate to a number, the call may evaluate to value at cost 1 where is the number of fractional days since 30 December 1899.
Rule (pi) states that PI() may evaluate to the mathematical constant at cost 1.
Rule (na) states that the function application NA() may evaluate to the error value #NA at cost 1. This is used to indicate that a value is not available or to indicate a late-bound parameter in a partially applied closure.
Rule (abs) states that if may evaluate to the number at cost then the call may evaluate to the absolute value of . Rule (asin) is similar to rule (abs) but may instead evaluate to the result of a call to the actual inverse trigonometric function .
Rules (not-1) and (not-2) handle the two different outcomes of the NOT function. Rule (not-1) states that if may evaluate to zero (false) at some cost then the call may evaluate to one (true); rule (not-2) handles the opposite case. Rule (ceiling) states that if the two argument expressions may evaluate to numbers then the expression may evaluate to 1 rounded to 2 decimal digits, as more precisely specified by the underlying function.
Rule (equal) is akin to rule (ceiling) except that it may evaluate to the equality comparison between the two numbers. We leave out the rules for other comparisons.
Rules (and-false) and (and-true) are inspired by rule (c5e) in Fig. 3, using reasoning analogous to that in Section 2.2. The point is that AND may use short-cut, speculative or parallel evaluation: once it finds that some argument evaluates to 0 (false), it may skip evaluating all the other arguments and immediately return 0. However, an implementation cannot reasonably be expected to guess beforehand which may evaluate to zero and evaluate only that, so the total cost should allow for the evaluation of some subset of arguments. Rule (and-false) says that if one can pick a subset of indices and there exists a ∈ such that evaluates to the number zero, then the result of AND may be zero (false). Rule (and-true) says that if all for ∈ may evaluate to a non-zero number value, then the result of AND may be one (true).
In either case, the total cost is the sum of the costs of the subset of expressions evaluated, plus one. The rules for OR are omitted as they are analogous, although dual, to those of AND.  We do not account for this generality here to avoid overcomplicating the rules.
Rule (const-array) says that if expression 1 may evaluate to a value at some cost 1 and expressions 2 and 3 may evaluate to non-negative numbers, then the call may evaluate to an array value of size 3 ⋅ 2 with 1 as the value of each element. The cost reflects that it is only necessary to evaluate 1 once.
Rule (choose) states that if 0 may evaluate to a number ∈ [1, + 1[, and = ⌊ ⌋, and may evaluate to value at cost , then the call may evaluate to at cost 1 + 0 + . Since CHOOSE is nonstrict, we require only that 0 and are evaluated. A more general rule, permitting also speculative evaluation of some , could adopt the same approach as for AND and have = {0, } as a special case.
Rule (columns) states that if may evaluate to an array value then a call to COLUMNS may evaluate to the width of that array value.
Notice that the cost is pessimistic; an implementation might be able to compute the width of an array value without first fully evaluating it, e.g. if the array value stems from a cell area.
Rule (index) states that if 1 may evaluate to an array value and 2 and 3 may evaluate to numbers within the bounds of the array value, then INDEX may evaluate to the value at index (⌊ 3 ⌋, ⌊ 2 ⌋). Like rule (columns), the cost is pessimistic. In rule (slice), the premises state that 1 may evaluate to an array value, expressions 2 and 4 may evaluate to a start-and end-column indices, and expressions 3 and 5 may evaluate to a start-and endrow indices, where the indices delimit a sub-array within the input array. Then SLICE may evaluate to an array value that is a slice of the original array. The sub-array's size is computed from the row and column indices and its elements are the values of the original array value. The work is one plus evaluating the four indices plus the work of evaluating the input array value plus the size of the new array. The cost is pessimistic; an implementation may return a view of the given (immutable) array value, in which case subcost 6 would be one or even zero.
Rule (iserror-true) states that if may evaluate to a value ∈ then the call may evaluate to 1. Rule (iserror-false) is complementary and handles the case where ∉ .
Rule (max) states that if all the argument expressions may evaluate to numbers at some corresponding costs, then the call may evaluate to the maximum of those values. The rule for MIN is analogous.
Rule (transpose) states that if the argument expression may evaluate to an array value of size ⋅ ℎ with cost , then the call may evaluate to a transposed array value of size ℎ ⋅ . Notice that element access has been swapped to in the resulting array. The cost is one plus the cost and the size of the resultant array. This cost is pessimistic; an implementation may represent array values in such a way that Rule (hcat) is closely related to the rule for HARRAY but concatenates its arguments, which is why there are additional premises to ensure compatible dimensions (equal heights) of the argument values. The other premises state that the expressions may evaluate to values at some associated costs as in rule (harray). The width of the new array value is the sum of the widths of all its arguments. The function ℎ is defined as follows; function ℎ ℎ is analogous: 1 otherwise The cost in the conclusion denotes the cost of concatenation and assumes an efficient implementation of array concatenation. The rule for VCAT is similar and has been omitted.

Rules for higher-order intrinsic functions
Since most higher-order functions call some supplied function multiple times, we introduce quantification over the environment ′ used to evaluate a sheet-defined function. For example, TABULATE calls the We quantify over ′ with the current position ( , ) as ′ to have a separate fresh environment, akin to a separate stack frame, for each call ( , ). Similarly, we also quantify over the function call cost environment ′ as ′ . We start by introducing the rule for TABULATE in full detail, then define auxiliary notation so that we do not need to repeat ourselves in the remaining rules.
), 1 + 1 + 2 + 3 + 4 + ⋅ ℎ Taking the premises in order from top to bottom, they state that 1 may evaluate to a function value, at cost 1 , that expects two more arguments, and that 2 and 3 may evaluate to non-negative numbers of ℎ rows and columns respectively, after truncation towards zero. We then postulate ⋅ ℎ fresh environments ′ where ≤ and ≤ ℎ, one for each application of the function value. The next quantified premises state that the input cells should contain the early-bound values ] except the last two arguments which must be the indices and of the function application. The following quantified premise states that for all cell addresses in the domain of ′ , excluding the set of input cells, the expression of each cell address may evaluate to the value given by environment ′ at some cost. The final premise states that each function application evaluates to the value of the function call's output cell. The call to TABULATE then evaluates to an array value of size ⋅ ℎ whose elements are the . The work is 1 plus the costs for evaluating the function value, the two arguments denoting the desired size of the array, the sum of the costs of applying the function for every position ( , ), and allocating a new array.
The premises that pass values to the function arguments, evaluate the cells of the function's body, get the result from the function's output cell and compute the total cost of evaluating the function are of a more general nature: this is how all higher-order functions call function values. So to reduce repetition, we define a predicate as follows: Note that the ''arguments'' and are the function call's result and its cost; these are ''output parameters'' of in much the same style as when a Prolog predicate is used to represent a function. The definition of can be used to rewrite the rule for TABULATE for a clearer and more compact rule: The REDUCE function takes three arguments: a two-argument function value ; an initial value 0 ; and an array value , and performs a reduction over the elements of using and the initial value 0 . The call REDUCE(CLOSURE("-"), 0, HCAT(1, 2, 3)) would compute , , ⊢ REDUCE( 1 , 2 , 3 ) ⇓ , 1 + 1 + 2 + 3 + We start with the top-level, expression-based rule (reduce) for the REDUCE function which passes the results of the evaluated expressions to a value-based rule. This rule in turn arbitrarily decomposes the array value until a single value is left which is then combined with the intermediate result using the function value. Expression 1 may evaluate to a function value, 2 to an initial value for the reduction, and 3 to an array 3 . Each value is passed as an argument to the value-based reduction rule defined next.
The inductive value-based reduction rule (reduce-inductive) first decomposes 3 into two arrays and . This can be an arbitrary decomposition as chosen by an implementation since, given an identity element and an associative binary function, a reduction may e.g. proceed from left to right using a decomposition similar to functional lists; or decompose the operations as a tree by recursively splitting the input array in halves to perform the reduction in parallel. Notice that we pass the result of the reduction of the left part of the decomposed array as the initial value of the reduction of the right part of the decomposed array. The reason is purely semantic and will become apparent shortly.
We need two additional base case rules to account for a reduction of a single value and for an empty array value. These are given as rules (reduce-base-singular) and (reduce-base-empty).
Rule (reduce-base-singular) handles the case where 3 is a singleelement array and we pass the single element 11 and starting value 2 to the sheet-defined function from the function value 1 . In rule (reduce-inductive), the accumulated intermediate results 2 and must be threaded through the subcomputations. Rule (reduce-base-empty) returns the starting value 2 of the reduction if passed the empty array.  2, 3, 4)) using a combination of the expression-and value-based rules for reduction. To illustrate these rules, we expand the derivation tree for the following expression in Fig. 14 and show the corresponding tree decomposition in Fig. 15.
In the example, is the cost of applying the addition operator, and ℎ is the cost of concatenating elements with HCAT. Additionally, we have omitted some of the set membership tests for values to keep the derivation tree succinct. For brevity, we denote lists in square braces [1,2,3,4]. The result of the expression is 8 + 1 + 2 + 3 + 4 = 18.
We are now ready to list the rules for the remaining higher-order functions in Funcalc which are shown in Fig. 16.
Rule (map) shows the rule for the MAP function which is in fact an -ary zip-by function, with the ordinary MAP function as the special case = 1. In general, if 0 evaluates to a function value, and the other arguments 1 , … , evaluate to array values of equal size, then for each array position ( , ) the function is applied to the arguments 1 , … , giving and the final result is the array of these . The total cost is one plus the cost of evaluating the function value, the cost of evaluating all the array value arguments, and the cost of all function applications.
Rule (colmap) states that if the first argument may evaluate to a function and the second argument may evaluate to an array value whose height equals 's arity, then the call may evaluate to a new single-row array value whose elements are the results of applying to each column in the input array. A similar function ROWMAP exists for row-wise mapping to a single-column array value.
Rule (countif) states that if the first argument 0 evaluates to a unary predicate function and the remaining expressions to some values, then a call to COUNTIF may evaluate to the number of those values for which returned a non-zero number (true). The total cost is one plus evaluating the function expression, all argument expressions and the cost of each function application.
Finally, we have the rule for HSCAN. The function performs a column-wise inclusive scan operation as opposed to an element-wise scan as per Blelloch [8]. For example, given function ( ) = + 1 we might call HSCAN as an array formula in the cell range A4:C5 on the cell range A1:A2 as shown in Fig. 17.
Except the starting column, the values in each column of the result are one greater than the corresponding values in the preceding column.
Rule (hscan) says that if 1 evaluates to a function value accepting one argument, and 2 evaluates to a column array value 0 , and 3 evaluates to a number , the result will be an array with + 1 columns, column number being the result of ( 0 ). The total cost is one plus the costs of evaluating the arguments plus the cost of the column concatenations plus the cost of the function applications.

Abstract cost semantics
Gomez et al. and Rosendahl worked on cost translations for higherorder functional languages [12,13] which cater for computing with unknown data values. Adding such unknown values allows for a rudimentary abstract interpretation of programs which in many cases can provide a rather precise approximation of the actual cost of the computation.
However, there is no standard way to insert an unknown value in a sheet. If a cell is empty and a lookup is performed on the cell, the value 0.0 is returned as is consistent with the standard semantics. However, it may be interesting to abstractly evaluate sheets where some cells have unknown values. This can be handled by a built-in function UNKNOWN taking no arguments and always returning the unknown value, denoted ⊤, which is the top-most abstract value in an abstract lattice. Calling UNKNOWN takes one computational step.
To allow computations with unknown values, we need an abstract representation of values that models the concrete values used in concrete interpretation or more generally we need a domain, which is a set equipped with a partial order, denoted ⊑. The domain has a least element, denoted ⊥ and if the domain is a lattice, it also has a top element, denoted ⊤. A domain is also equipped with a join operation, denoted ⊔ [27]. Fig. 18 gives a suggested lattice of abstract values for Funcalc which can be used in the definition of abstract values computed by an abstract semantics for Funcalc. The sets and maps used for abstract interpretation are shown in Fig. 19 and the abstract cost semantics rules are depicted in Fig. 20.
The ordering on is such that ∀ ∈ . ⊑ ⊤. Furthermore, ∀ ∈ . ⊑ (⊤, ⊤) where denotes the set of all possible abstract array values. We can now follow the ideas presented by Schmidt [27] and provide a tracebased abstract interpretation for Funcalc, based on the ideas for big step semantics presented in section 5 of [27].
The cost semantics for Funcalc presented in Section 3 is extended with the following rules: Rule (u1a) states that calling the built-in function UNKNOWN, taking no arguments, returns the unknown value ⊤ with a cost of one computational step.
Rule (g3a) states that if the predicate, i.e. the first argument to IF, evaluates to the unknown value ⊤ at some cost 1 , then the resulting value is the join of the values, denoted by ⊔, of the evaluation of the two branches, and the total cost of evaluating IF is the cost 1 plus the maximum of the cost of evaluating either of the two branches.
Rule (g5a) states that if any argument to a built-in function evaluates to the unknown value ⊤, then the resulting value is the unknown value ⊤, i.e. all built-in functions are strict wrt. the unknown value ⊤. However, the cost of calling a built-in function with the unknown value ⊤ is still the cost of evaluating the arguments, plus the cost of the function call and the cost of the work of the built-in function. This is a bit conservative, but allows the implementation of built-in functions to do some work before returning the unknown value ⊤.
Rule (g10a) states that if the function in an APPLY, i.e. the first argument, evaluates to the unknown value ⊤ then the resulting value  is the unknown value ⊤, and the cost is the top element in the cost domain, i.e. ∞. The standard semantics for APPLY does not state an evaluation order, but APPLY is strict in all its arguments. An alternative rule for APPLY could be stated as follows: APPLY, thereby excluding an implementation using short-cut semantics for the first argument. Since a short-cut semantics is a quite natural implementation strategy, we have chosen to use the rule (g10a) instead of rule (g10alt) as the resulting cost is the same, namely ∞. With these rules, it is possible to establish a safety property for finite derivations [27]. First we define a safety property for values and costs: , safe , ⊑ ⊑ Here we use the ordering on values defined above and the usual order on integer costs augmented with ∀ . ≤ ∞. The property states that value and cost are safely approximated by abstract value and abstract cost if and only if is ordered above in the abstract value lattice and is ordered above , respectively. As mentioned in [28] this relation is a Galois connection between the concrete and the abstract domains.
The safety property on values is then extended to environments: , safe , Finally the safety property can be extended to judgments.
where ⇓ indicates that the transition ⇓ is established using the rules from Section 3 and ⇓ indicate that transition ⇓ is established also using the additional abstract rules presented above.
With the safety property on judgments we can extend the definition to trees safe . For trees resp. the proposition safe holds if ( ) safe ( ) and for every child subtree of there exists a subtree of such that safe holds. Let wftree and wftree be the set of well formed proof trees in the concrete semantics, respectively the set of well formed proof trees in the abstract semantics. For every expression and concrete environments , and abstract environments , , where , safe , holds, we can establish the desired property that for every proof tree ∈ wftree , where root( ) = , ⊢ ⇓ , , and for every proof tree ∈ wftree , where root( ) = , ⊢ ⇓ , , it is the case that safe . The proof of this follows by induction on the height of the derivation tree.   The only non-trivial base case is when rule (u1a) has been applied.
Cells filled with a call to the built-in function UNKNOWN are filled with a constant value ''in a production sheet''. Any constant value is also an element of and since the ordering on is such that ∀ ∈ . ⊑ ⊤ this base case holds. Of the three inductive cases (g3a), (g5a) and (g10a), rule (g3a), the rule for ( 1 , 2 , 3 ), is the most interesting. This rule is only applicable if the condition 1 cannot be evaluated to a concrete value in the abstract cost semantics, i.e. 1 evaluates to ⊤. If the concrete value evaluates to 0.0 in the concrete semantics, the rule (g3f) will be applied in the concrete semantics yielding a value 3 and a cost = 1+ 1 + 3 where 1 is the cost of evaluating the condition 1 , and 3 is the cost of evaluating the falsebranch 3 . Clearly 3 ⊑ 2 ⊔ 3 and 1+ 1 + 3 ⊑ 1+ 1 + ( 2 , 3 ), where 2 is the value of evaluating the true-branch and 2 is the associated cost. The case when 1 evaluates to a value different from 0.0 is similar.
The above only establishes a safety property for finite derivations, i.e. for terminating programs. Not all programs terminate and future work needs to look into handling infinite derivations as well, possibly following ideas presented in [27].

Implementation of cost semantics
Before we discuss the full implementation details of the various sections presented thus far, we give a brief introduction to some of the inner workings of the research spreadsheet application Funcalc deemed necessary for understanding the implementation. Readers interested in learning more are encouraged to read [7]. Funcalc's interpreter implements two interfaces for evaluating cells (ICellEvaluator) and expressions (IExpressionEvaluator), the former interface is given in Listing 1 with some details omitted. Evaluation of a cell happens in the context of some column and row in some sheet. These interfaces can be implemented by any class that needs to operate on cells, expressions or both, and are used to implement the rules of our cost semantics. T Eval ( B l a n k C e l l c e l l , Sheet s h e e t , i n t col , i n t row ) ; 5 T Eval ( Formula c e l l , Sheet s h e e t , i n t col , i n t row ) ; 6 T Eval ( NumberCell c e l l , Sheet s h e e t , i n t col , i n t row ) ; 7 T Eval ( T e x t C e l l c e l l , Sheet s h e e t , i n t col , i n t row ) ; 8 }

Listing 1: The interface for evaluating cells in Funcalc.
Instead of returning a value like the standard interpreter, the cost interpreter returns a CostResult consisting of both the Value and its cost. We define some auxiliary functions like MakeCostResult which constructs a CostResult tuple from a pre-existing CostResult or from a value and a cost. These auxiliary functions ensure that cost results are monotonically increasing by adding a unit cost of 1.
We have implemented two variants of the cost semantics in Funcalc: a concrete cost evaluator (Section 6.2) and an abstract cost interpreter (Section 6.4). The former uses unit costs and is not guaranteed to terminate e.g. in the presence of infinite recursion, the latter is inspired by [27,29,30]. We also discuss a few important details regarding cost evaluation of sheet-defined functions.

Cost evaluator implementation
The implementation of the cost evaluator follows the semantic cost rules closely as shown in Listing 2 for the simplified implementation of the cost evaluation of IF (see rules ( 3 ), ( 3 ) and ( 3 ) in Section 2.1). The evaluation function EvalIf takes the function call expression FunCall representing the IF expression and the column, row and sheet of the cell. First, we check if the function call consists of three subexpressions (a condition and two branches). If not, we return an error indicating an incorrect number of arguments. Otherwise, we evaluate the conditional expression. If the result is an error value, we shortcircuit as per rule ( 3 ) and return the result of the condition (the error) and the cost obtained so far. Otherwise, we cast the result of the condition to a number. If the cast fails, we return an error indicating an argument type error and the cost obtained thus far. If the condition is indeed a number, we pick the appropriate branch and evaluate the expression as per rules ( 3 ) or ( 3 ), then return its value along with the cost of evaluating the condition and the given branch expression.
As an example, EvalIf would return a cost result consisting of the value SIN(1+2)≊ 0.14112 at cost 6 for the following expression:

=IF(1, SIN(1+2), COS(3))
Evaluation of the IF function call and its condition costs 2 units. The inner function call to SIN costs four units: one for the SIN function application, one for the + operator application and one for each of the arguments of the addition.

Evaluation of sheet-defined functions
In Funcalc, sheet-defined functions are not interpreted but automatically compiled to CIL bytecode [7]. Without extending the semantics to incorporate CIL bytecode, we cannot use the existing interpreter framework to find the cost of evaluating sheet-defined functions. We could generate additional code to compute costs but this seems like an excessive and complicated approach. Instead, we directly interpret the cells of a sheet-defined function using the cost evaluator by evaluating the output cell of a sheet-defined function and following dependencies back to its input cells. This requires proper abstraction of ∶ → , that is, the local cell environment or stack frame of a sheetdefined function, to handle both recursive sheet-defined functions and normal function calls. Consider the definition of the factorial function in Sheet 1.
To implement , we could directly modify the input cells of the sheet-defined function on each call but this would temporarily modify cells in the spreadsheet which could easily lead to inconsistencies if we are not careful. Instead, we keep track of an internal, local environment ∶ → that mimics . When a sheet-defined function is called, we create and push a new local environment onto an internal stack and store the sheet-defined function's parameters in it by mapping the addresses of the input cells to their respective parameter values. This mimics the semantic rule (g8) for application, where the input parameters for the current function call are stored in a fresh environment ′ i.e. ′ ( 1 ) = 1 … ′ ( ) = . When calling a function recursively, we create and push a new local environment with the new parameters. When the recursive call returns, we pop the top-most local environment from the stack. Therefore, behaves exactly like a stack frame. Lastly, cost evaluation of a cell reference is modified to first perform a lookup of the address in the top-most local environment, if any, before examining the cells of the actual sheets. Thus when we do computation in some recursive sheet-defined function and need to evaluate an input parameter, we first look in the local environment and not the actual spreadsheet.
The above local environment scheme combined with evaluating the output cell first, ensures that we only evaluate the cells that are necessary for computing the output cell as discussed in Section 3.2. Argument evaluation for intrinsic functions is implemented in a left to right order in the interpreter. This removes the nondeterminism afforded by the error rule (c5e), as evaluation of arguments will be terminated when an argument evaluates to an error, thus fixing = {1, … , } as the least prefix of argument indexes for which is an error.
Interestingly, if we were to strip away any notion of cost from the evaluation of sheet-defined functions, we have in fact implemented a full-fledged sheet-defined function interpreter which is likely what Funcalc would have used if there was no sheet-defined function compiler. One issue with the above approach is that the cost of interpretation and the cost of bytecode execution may not correlate, since the point of compiling sheet-defined functions is that bytecode execution should be much faster than interpretation of expressions. This is not a problem as long as cost is only used as a measure of computational steps. However, if we were interested in worst case execution times, tighter correspondence with the execution time of CIL bytecode becomes paramount.

Abstract cost evaluator implementation
This section presents examples from the abstract cost semantics implementation. The abstract cost implementation introduces a new type of value, Top to represent ⊤ values as discussed in Section 5; recall that ⊤ represents unknown values, such as input values for the spreadsheet, and values that through computation depend on ⊤.
Essentially, this is just a subclass Top of Value; a function UNKNOWN is introduced to produce a ⊤ value.
The abstract cost implementation is an implementation of the evaluator interfaces, and is a modified version of the CostEvaluator. In this modification, if the condition is ⊤, the resulting value is a join of the values of both branches, with cost of the expensive branch with an added cost of the condition-evaluation cost plus the unit cost of the if-expression. Otherwise, the result is the result of evaluation by the CostEvaluator implementation. Table 1 The total concrete cost, total abstract cost, overapproximation in the abstract cost evaluation, number of formula cells and the time taken to evaluate the cost of all cells in the LibreOffice Calc and EUSES spreadsheets. The cost evaluation was run twenty times and the average of those runs are shown in the Runtime column.

Spreadsheet
Concrete cost Abstract cost Overapprox.

Formulas Runtime
LibreOffice Calc (runtime in seconds)

building-design
Since the concrete cost evaluator costs are proportional to the number of operations of an expression or alternatively the number of rule applications, we are not particularly interested in the precision of the costs since they are not an estimation of the actual running time of the spreadsheet. As we mentioned in Section 2.1, costs based on measurements of real machine execution could lead to more realistic costs. Instead, we are interested in how long it takes to compute the cost of each cell in a spreadsheet. Table 1 contains the costs, number of formula cells and time taken to compute the cost of all cells in six spreadsheets from LibreOffice Calc [31] and a subset of the EUSES corpus [32]. The costs correspond to applying the function to each cell address in the spreadsheet as presented in Section 3.4.

Abstract cost evaluator results
In ordinary spreadsheets, there are no unknown values resulting in an abstract calculation, i.e. ⊤ values produced by the UNKNOWN function, so in this case the abstract cost evaluator would compute the same values and costs as the concrete cost evaluator. Therefore, in our evaluation of the abstract cost evaluator, we replace all constants in the spreadsheet with ⊤, before the abstract cost evaluator is run. The results are found in Table 1. Because some conditional values related to recursive calls are ⊤, some cost-results are ∞, because of infinite recursion. The largest overapproximation is found in WasteCalen-darCalculat#A843B. This is caused by a large number of IFs, of the form: IF(T11>-1, 0, IF(T11<1, (S11-F11)/F11, 0)) where T11 is ⊤ in the abstract version. Other spreadsheets have either costbalanced branchings or the most expensive branch is also taken in concrete evaluation.

Discussion
At a glance, we notice that there seems to be no correlation between the number of formula cells and the time taken to evaluate the cost of each cell in the spreadsheet. This is to be expected as the formula count does not tell us anything about the complexity of each one.
For example, the ny_emit99 and Time spreadsheets have almost the same number of formula cells but vastly different concrete costs and runtime.

Conclusion and future work
A precise cost semantics was presented in Section 3 and in Section 4. The cost evaluation semantics for Funcalc was extended to compute with unknown values in Section 5, which serves as a first step towards an approximate cost analysis, based on abstract interpretation. Finally, implementations for the concrete and abstract cost semantics were presented in Section 6.
The purpose of the cost semantics and calculations is to serve as a guide for load-balancing parallel computations in spreadsheets, e.g. via task partitioning for execution on multi-core CPUs [9] or off-loading work to GPGPUs [2]. Moreover, the evaluation and cost semantics may serve to improve the understanding of spreadsheet computations in general and the safety of and reliance on a given implementation. Also, they may be used to prove that optimizations preserve the meaning of spreadsheet computation and that these optimizations reduce the amount of work needed to perform a computation. The cost calculations are based on counting computational steps. It would be possible to extend the cost calculations to approximate runtime execution time by measuring the execution time of each basic computational step on a given computer and use this information as a factor in the equations. Clearly such calculations are platform dependent.
The approximate abstract cost analysis is a first step towards a more general framework of abstract interpretation of spreadsheet expressions. The safety property, presented in Section 5, was established directly between the concrete cost semantics and the approximate abstract cost semantics following ideas from [27], albeit only for terminating derivations. Although the approximate abstract cost analysis can be seen as a rudimentary abstract interpretation, we did not apply the ''standard'' approach to establishing correctness for abstract interpretations. Usually a collecting semantics is the starting point for a sequence of more and more abstract semantics in abstract interpretation which eventually leads to an implementable analysis, i.e. a semantics-based description that can be turned into an algorithm implementing the desired analysis. In the standard setting, safety and correctness of the analysis is established via Galois connections and widening operators [33]. We expect to follow this approach when generalizing our work to a more generic framework for abstract interpretation of spreadsheets.
Due to the higher-order nature of Funcalc, another future development would be a closure analysis to improve cost estimates of function application.
Another future development may be to give a precise semantics for depth (also called span or critical path length) i.e. the length of the longest sequential dependence, for parallel evaluation in the sense of Blelloch [8]. This could be used as basis for an abstract interpretation to estimate depth in addition to the work defined in this paper.
Finally, one could also imagine tools, based in the formal semantics, for analyzing or verifying various aspects of spreadsheets. One such tool could be a tool to formally verify the correctness of the spreadsheet program. Another tool could guide users through performance bottlenecks in a spreadsheet and even suggest possible improvements.