MikaelMayer / Sketch-n-Script

The official repository for issues about Reversible Formulas - Sketch-n-Script, the Google docs add-on
1 stars 1 forks source link

Variable names #4

Closed phildionne closed 5 years ago

phildionne commented 5 years ago

Thanks for building this, it's filling such an important gap.

Regarding variable names, it seems like currently the only variable name cases supported are:

this_variable
thisVariable

Would it be possible to support these cases as well?

this-variable
this.variable
this/variable

When you have lots of variables, it's nice to namespace them visually.

MikaelMayer commented 5 years ago

Wow thanks for opening the first issue of Sketch-n-Script!

Short answer: No

  1. The names both in definition and formulas follow the same syntax as JavaScript (formulas can be arbitrary JavaScript), so it's not possible to redefine what names are allowed.
  2. Besides, this is a reserved keyword so you cannot assign it a value.
  3. How would you disambiguate =k/v, which could be 1) A division of k by some variable v 2) A division of the value of k by v and 3) a variable name k/v ? Same for minus.

Long answer: You can. We could.

There are two loopholes we / you can exploit to have nested names.

Use nested objects

self = ({
variable: "Hello",
othervar: "world"})

=(self.variable) =(self.othervar)

Note that you have to put parentheses because the expression is not just a variable. We might remove the need of parentheses if we find out there is huge need for this kind of expression.
The above variant does not benefit (yet) from the reversibility feature (stay tuned).

Assign properties to objects

self = ({})

=(self["variable"] = "Hello") =(self["othervar"] = "World")
=(self["variable"]) =(self["othervar"])

This variant assigns the properties to self

Suggest JavaScript-compatible changes.

Although JavaScript is the language behind, the first interpretation of a formula is still arbitrary. For example, you can currently assign a name to the result of a formula using =name@(formula). We could thus rewrite internally, like Scala is doing:

self.variable = ...
self-othervar = ...
self/othervar = ...

to

self = (__assign(self, {variable: ....})
self$dashothervar = ...
self$slashothervar = ...

and to refer to these variables, you would use:

=self.variable
=self$dashothervar  or =k("self-othervar")
=self$slashothervar  or =k("self/othervar")

where

k = (function(s) { return s.replace(/-/g, "$dash").replace(/\//g, "$slash"); })

I would be curious to see a document using so many different variables names. If you don't mind, would you be able to share with me an anonymised template demonstrating such usage? @ravichugh and I would love to use it for research purpose.

phildionne commented 5 years ago

Thanks for the in-depth explanation. I really like the nested object approach, it's explicit and doesn't feel too hacky.

Regarding an example, I can quickly share with you a sample of the variables we are using. We're just getting started with this project so we don't have a fully templatized example yet, but I can see how large it could grow. I expect a single template of ours to require between 500 and 4000 variables.

organization_business_type = ("corporation")
organization_financial_year_end_day = ("31")
organization_financial_year_end_month = ("12")
organization_legal_name = (1)
organization_name = ("Test")
engagement_mandated = ("false")
engagement_mandated_at = ("")
engagement_period_end = ("2019-04-01 00:00:00 UTC")
engagement_period_start = ("2018-04-01 00:00:00 UTC")
engagement_previous_period_end = ("2018-03-31 00:00:00 UTC")
engagement_previous_period_start = ("2017-04-01 00:00:00 UTC")
engagement_standard = ("aspe")
account_plants_and_soil = (1)
account_rent_or_lease = (1)
account_opening_balance_equity = (1)
account_savings = (1)
account_plants_and_soil = (-1)
account_services = (-1)
account_original_cost = (1)
account_loan_payable = (-1)
account_telephone = (1)
account_retained_earnings = (-1)
account_discounts_given = (1)
account_accounts_payable_a_p = (-1)
account_landscaping_services = (-1)
account_miscellaneous = (1)
account_sales_of_product_income = (-1)
account_sprinklers_and_drip_systems = (-1)
account_pest_control_services = (-1)
account_mastercard = (-1)
account_office_expenses = (1)
account_checking = (1)
account_decks_and_patios = (1)
account_board_of_equalization_payable = (-1)
account_cost_of_goods_sold = (1)
account_fountains_and_garden_lighting = (-1)
account_inventory_asset = (1)
account_undeposited_funds = (1)
account_notes_payable = (-1)
account_lawyer = (1)
account_legal_professional_fees = (1)
account_dept_of_revenue_payable = (1)
account_maintenance_and_repair = (1)
account_accounts_receivable_a_r = (1)
account_accounting = (1)
account_maintenance_and_repair = (-1)
account_gas_and_electric = (1)
account_advertising = (1)
account_installation = (-1)
account_equipment_rental = (1)
account_automobile = (1)
account_design_income = (-1)
account_job_expenses = (1)
account_equipment_repairs = (1)
account_fuel = (1)
account_insurance = (1)
account_sprinklers_and_drip_systems = (1)
account_meals_and_entertainment = (1)
account_bookkeeper = (1)
account_plants_and_soil_formatted = ("1")
account_rent_or_lease_formatted = ("1")
account_opening_balance_equity_formatted = ("1")
account_savings_formatted = ("1")
account_plants_and_soil_formatted = ("-1")
account_services_formatted = ("-1")
account_original_cost_formatted = ("1")
account_loan_payable_formatted = ("-1")
account_telephone_formatted = ("1")
account_retained_earnings_formatted = ("-1")
account_discounts_given_formatted = ("1")
account_accounts_payable_a_p_formatted = ("-1")
account_landscaping_services_formatted = ("-1")
account_miscellaneous_formatted = ("1")
account_sales_of_product_income_formatted = ("-1")
account_sprinklers_and_drip_systems_formatted = ("-1")
account_pest_control_services_formatted = ("-1")
account_mastercard_formatted = ("-1")
account_office_expenses_formatted = ("1")
account_checking_formatted = ("1")
account_decks_and_patios_formatted = ("1")
account_board_of_equalization_payable_formatted = ("-1")
account_cost_of_goods_sold_formatted = ("1")
account_fountains_and_garden_lighting_formatted = ("-1")
account_inventory_asset_formatted = ("1")
account_undeposited_funds_formatted = ("1")
account_notes_payable_formatted = ("-1")
account_lawyer_formatted = ("1")
account_legal_professional_fees_formatted = ("1")
account_arizona_dept_of_revenue_payable_formatted = ("1")
account_maintenance_and_repair_formatted = ("1")
account_accounts_receivable_a_r_formatted = ("1")
account_accounting_formatted = ("1")
account_maintenance_and_repair_formatted = ("-1")
account_gas_and_electric_formatted = ("1")
account_advertising_formatted = ("1")
account_installation_formatted = ("-1")
account_equipment_rental_formatted = ("1")
account_automobile_formatted = ("1")
account_design_income_formatted = ("-1")
account_job_expenses_formatted = ("1")
account_equipment_repairs_formatted = ("1")
account_fuel_formatted = ("1")
account_insurance_formatted = ("1")
account_sprinklers_and_drip_systems_formatted = ("1")
account_meals_and_entertainment_formatted = ("1")
account_bookkeeper_formatted = ("1")
fsli_total_current_asset = (1)
fsli_total_noncurrent_asset = (1)
fsli_total_retained_earning = (1)
fsli_total_equity = (1)
fsli_total_current_liability = (1)
fsli_total_noncurrent_liability = (1)
fsli_total_tax_expense = (1)
fsli_total_other_revenue_and_expense = (1)
fsli_total_expense = (1)
fsli_total_cost_of_revenue = (1)
fsli_total_revenue = (1)
fsli_total_current_asset_formatted = ("1")
fsli_total_noncurrent_asset_formatted = ("1")
fsli_total_retained_earning_formatted = ("1")
fsli_total_equity_formatted = ("1")
fsli_total_current_liability_formatted = ("1")
fsli_total_noncurrent_liability_formatted = ("1")
fsli_total_tax_expense_formatted = ("1")
fsli_total_other_revenue_and_expense_formatted = ("1")
fsli_total_expense_formatted = ("1")
fsli_total_cost_of_revenue_formatted = ("1")
fsli_total_revenue_formatted = ("1")
fsli_noncurrent_asset_accumulated_depreciation_leasehold_improvement = (1)
fsli_current_asset_advance_receivables = (1)
fsli_current_asset_advance_to_shareholders = (1)
fsli_noncurrent_asset_accumulated_depreciation_leasehold_improvement_formatted = ("1")
fsli_current_asset_advance_receivables_formatted = ("1")
fsli_current_asset_advance_to_shareholders_formatted = ("1")

A few tricks we're doing:

Another point worth mentioning: I'm basically using this project as an improvement over https://developers.google.com/docs/api/how-tos/merge as it allows me to use =sum() which is allows me to output numerical variables and compute totals in the template.

MikaelMayer commented 5 years ago

That's a very interesting approach. Did you know the following in Sketch-n-Script:

  1. You can put all these variables in the text area on the plugin's sidebar so that it's hidden from the doc.

  2. In the most recent versions, parentheses around expressions are not necessary for 1) numbers 2) booleans (true and false) 3) Lists delimited by square brackets. The reason for 1) and 2) is that their toString conversion is an almost 1-to-1 with the definition. So:

    account_retained_earnings_formatted = (-1)
    engagement_mandated = (false)

    is the same as

    account_retained_earnings_formatted = -1
    engagement_mandated = false
  3. You don't need parentheses if the expression on the right should be interpreted as a raw string, e.g.

    organization_business_type = corporation

    is the same as

    organization_business_type = ("corporation")

Also, you said you were going to use this project as an improvement over mail merge. I'm curious, how are you doing this? Are you just going to generate documents, and then run the script on each document manually? Or would you like to generate all the merge into a single document? We are currently working on making the second option feasible, by selecting the content and abstracting over it, so we can reuse it with different parameters.

phildionne commented 5 years ago

Thanks for the in-depth explanation, however I've just updated the way we're doing things based on your previous suggestion.

Before going further, I think explaining our use-case would be helpful:

  1. We have N projects
  2. Each project must use X (where 5 < X < 15) Google Docs documents
  3. To generate the X Google Docs documents for a given project, a user must copy the canonical Google Docs documents and start Sketch-n-Script
  4. The user goes in our application and copy a payload (see attached)
  5. For every document, the user paste the payload in the plugin's hidden form
  6. For every document, the user generates the evaluated document

The attached screenshot comes from a prototype we're working on. It shows formulas and variables available to the user for a given project. I like keeping the variables in a JS object, it opens up possibilities to iterate, filter, etc. on variables, especially since we have lots of numerical values.

Also, it's nice to ship formatting logic (see the format key in the screenshot) straight in the payload, it should reduce errors on our end.

55415757-9ef81280-553b-11e9-9e1e-7fd94e83b06a

Are you just going to generate documents, and then run the script on each document manually? Or would you like to generate all the merge into a single document?

In the best scenario:

  1. Upon starting a new project, our application logic would duplicate Google Docs templatized documents via the GDocs API
  2. For a given project, every time data changes in our application, the application would trigger a re-render of the associated Google Docs documents
  3. The re-render would basically involve the Sketch-n-Script plugin receiving a JSON payload and trigger an evaluation.

To be considered:

Hope this helps, happy to detail further.