Skip to content

Instantly share code, notes, and snippets.

@chrispsn
Last active December 4, 2018 13:04
Show Gist options
  • Save chrispsn/822bf60dd262438e48cbb795899d30a3 to your computer and use it in GitHub Desktop.
Save chrispsn/822bf60dd262438e48cbb795899d30a3 to your computer and use it in GitHub Desktop.
Putting functions into cells in Mesh sheets

Putting functions into cells in Mesh sheets

Right now, when you write something in the formula bar and press Enter, one of two things will happen, depending on whether you prefixed it with =:

  • if you didn't, Mesh will run the contents of the formula bar through a series of regexes and try to transform your input into a JavaScript equivalent (string, number, percentage, date, etc).
  • if you did, Mesh will defer evaluating that input by writing a function to that cell, with the stuff after the = as the return value. When another cell references that cell, the Mesh calc engine will then call that function and use its return value.

So:

  • 2018-11-04 would be transformed into new Date(2018, 11, 4)
  • =2018-11-04 would be written directly, in this case resulting in a calculation (returning 2003).

That has worked fine so far, except when you want to assign a function value to the cell - as in, an abstract unit of logic, something that can be fed into other functions as opposed to a mere deferred calculation.

Now, in general, Mesh sheets will not encourage creation of functions. I am hoping the upcoming stream values into Mesh sheets feature will provide a sufficient backbone for testing and scenario analysis.

However, in some cases, functions may be handy. One example could be arranging complex conditional logic into a table, so that each 'procedure' sits in its own table cell or row. The Mesh app itself does this:

Regex table

So: how do you distinguish between functions that should be returned as values and functions that are mere deferred calculations? This comes up twice:

  • the source representation, and
  • the user's formula bar input.

Source representation

Mesh cells containing functions, actually contain a function that returns another function. This results in an unnecessary layer of nesting; for example:

Table_ChangeCellValue: {
    v: function() {
        return function(table_np, row_index, col_header, new_value) {
            const rows_np = Table_GetRowsArray(table_np);
            const row_np = rows_np.get("elements", row_index);
            let item_np = Object_GetItem(row_np, col_header);
            if (item_np === undefined) {
                Object_InsertItem(row_np, col_header, "null");
                item_np = Object_GetItem(row_np, col_header);
            }
            Object_ReplaceItemValue(item_np, new_value)
        }
    },
    l: [32, 22]
}

That works, but it's a bit verbose. Can it be better? How could we tell Mesh to just write a function, and not try to calculate it as part of the calc chain?

Well, what if we used getter literals for cells that need to be calculated? Then the Mesh calc engine has the following rules:

  • If you see the cell prop is a getter (via GetOwnPropertyDescriptor), call it and return it.
  • Otherwise, just return the cell's value (be it a function or otherwise).

So in the source you'd have (omitting other props):

RegularCell: {v: 123}
FormulaCell: {v: function(a, b) {return a + b}} // same as RegularCell
GetterCell:  {get v() {return 123 + 456}}

Formula bar input

How would you tell Mesh to write a function without making it a getter (ie, without using a = prefix in the formula bar)?

Some ideas:

  1. use a different prefix
  2. just write 'function' at the start and have the 'easy data entry' regexes let that case through raw
  3. have another special syntax that indicates a function.

I suspect we'll go with #2: it's easy to do within the existing code, and it's the most familiar to existing JS users.

But I quite like #3 as well, because it gives us a way to use a much briefer function syntax. Maybe:

  • emulating ES6 arrow literals, ie (x, y) => x + y? (although it could make people confused because it wouldn't share the same properties for this)
  • emulating K-like function literals?
    • {x + y} (implicit params x, y, z; turns into function(x, y) {return x + y})
    • (a, b){const c = a + b; c ^ 2} (custom params; implicit return for last statement)
  • something else? eg {a,b | const c=a+b; return c^2}

The downside to #3 is that you couldn't use that cool syntax in formulas. Incidentally we also run into this issue with the 'emulated' date literals... ie you won't always get a date if you write 2018-11-04. But Excel has this issue too, so no biggie.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment