Forum Discussion

joelb95's avatar
joelb95
Brass Contributor
Apr 15, 2024

Thunking vs. Stuffing - Is there a difference?

When I mentioned the idea of stuffing to PeterBartholomew1 in a different context, he asked me what the difference was between stuffing and thunking.  I did not have an immediate answer for him and haven't had the chance to come up with one.  In the meanwhile, I am curious if any of you have an opinion.  

 

Here are my two primary definitions:

 

A "thunk"  -  lambda(x, lambda(x))

"Stuffing" -

1) (the preferred version on my view) lambda(x, lambda(choice, choose(1, x)) 

or

2) (the thunk like version) lambda(x, lambda(choose(1,x))

 

The way they are created and have their values returned is as follows:

 

For the thunk:

 

=LET(
value, SEQUENCE(3,3), 
thunk, LAMBDA(x, LAMBDA(x)), 
thunk1, thunk(value), 
thunk1_return, thunk1(), 
thunk1_return)

 

For the preferred "stuff"

 

=LET(
value, SEQUENCE(3,3), 
stuff, LAMBDA(x, LAMBDA(y, CHOOSE(1, x))), 
stuffed_pref1, stuff(value), 
stuffed_pref1_return, stuffed_pref1(5), 
stuffed_pref1_return)

 

 Where stuffed_pref1 can take any value (which is taken advantage of in other contexts).

 

Here is an example of how I use stuffing for stacking dynamically generated arrays and an edited excerpt from my note to him.  You'll notice that I include an "unstuff" lambda that exposes the previously stuffed element.  In this way, I now have a simple verb to describe the process and make explicit what is going on rather than relying on potentially ambiguous usage of variable_name = thunk(element) and variable_name() to guard and unguard an element.
______________

 

For the sake of brevity, here is a reduced version of some of the thinking. I hope my variable names make it clear what is going on, but here is a quick description - I encourage you to change the last line to each of the variable names.

 

First, I define the two base functions - "stuff" to guard an array/element and "unstuff" to unguard an array/element.


Second, I create two arrays of three elements (one as a row, and the other a column).


Third, I demonstrate the stuffing and unstuffing of these arrays - when stuffed you get a calc error, when unstuffed you get the array back.


Fourth, I stack the two guarded arrays and then demonstrate that by stuffing the stacked guarded arrays, I create a list of arrays that can be passed around and itered over (not to mention be expanded, sliced, etc.)!


Fifth, I give you the "secret" iter formula that you can parametrize to accept any function you wish to iter over, but I did not want to overwhelm you with yet another abstraction.


Sixth, I iter over the two-element list and vstack the original arrays expanded to have the same width. This is the result you see if you just copy/paste the formula.


Seventh, I create a third array (which is dynamically sized) add it to the original two element list, and then iter over the new three element list.

 

In my own module I obviously have these various functions named and segregated as discrete lambdas, but I wanted you to be able to just paste the formula as is and see the magic. Using this method, you can completely replace excel's "groupby" and "pivotby" functions with vastly better customization options. I can, for instance, filter a two-dimensional array by multiple options, pivot by whatever I like, and add "custom" columns to the end - all with specifiable column names, row "totals", and column totals. I can also sort by a desired column and will ultimately be able to sort by multiple columns.

 

=LET(
    ex_array_row, {2, 3, 4},
    ex_array_col, {"a"; "b"; "c"},
    stuff, LAMBDA(target_array, LAMBDA(y, CHOOSE(1, target_array))),
    unstuff, LAMBDA(stuffed_target_array, stuffed_target_array(1)),
    stuffed_ex_array_row, stuff(ex_array_row),
    unstuffed_ex_array_row, unstuff(stuffed_ex_array_row),
    stuffed_ex_array_col, stuff(ex_array_col),
    unstuffed_ex_array_col, unstuff(stuffed_ex_array_col),
    row_with_stuffed_elements, HSTACK(stuffed_ex_array_row, stuffed_ex_array_col),
    THIS_IS_A_LIST_OF_ARRAYS, stuff(row_with_stuffed_elements),
    secret_list_iter, LAMBDA(LIST,
        LET(
            unstuffed_list, unstuff(LIST),
            num, COLUMNS(unstuffed_list),
            first_element, unstuff(INDEX(unstuffed_list, 1, 1)),
            recursive_case, LAMBDA(self, n,
                IF(
                    n = 1,
                    first_element,
                    LET(
                        current_element, unstuff(INDEX(unstuffed_list, 1, n)),
                        prior_elements, self(self, n - 1),
                        width, MAX(
                            COLUMNS(current_element),
                            COLUMNS(prior_elements)
                        ),
                        result, VSTACK(
                            EXPAND(prior_elements, , width, ""),
                            EXPAND(current_element, , width, "")
                        ),
                        result
                    )
                )
            ),
            result, recursive_case(recursive_case, num),
            result
        )
    ),
    vstack_list_elements, secret_list_iter(THIS_IS_A_LIST_OF_ARRAYS),
    ex_array_3, SEQUENCE(4, 4),
    stuffed_ex_array_3, stuff(ex_array_3),
    expanded_list, stuff(
        HSTACK(unstuff(THIS_IS_A_LIST_OF_ARRAYS), stuffed_ex_array_3)
    ),
    vstack_new_list_elements, secret_list_iter(expanded_list),
    vstack_list_elements
)

 

  • joelb95's avatar
    joelb95
    Brass Contributor

    Here is my initial take - a thunk is a conceptual tool associated with delaying execution of a function, i.e. delaying a verb. The stuffing is about making data callable, i.e. verbifying a noun. On an abstract level, the goal of the two methods is not the same - the thunk is about when to create data while the stuffing is about what to do with the data once created - use it once, us it now, use it multiple times, use it later, etc.

     

    Facially the base case of "store this data" appears to be addressed by the alternative syntaxes, but the stuffing is immediately at home in the world of composition/functional programming idioms. If nothing else, by avoiding "thunks" you don't necessarily invoke the conceptual baggage that comes with them.

     

    What I can't articulate, however, is if there is any evaluative/programmatic distinction between them. I'm looking for the dirt at the margins of excel implementation/execution. The sort of stuff that djclements implicated when explaining how byrows operating over a choose formula is not exactly what you might imagine it to be.  Is there any context in which a basic "guard this element and expose it when called" is better served (or made worse off) by the stuff formulation rather than the thunk one?

Resources