Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Notepad/soulver-like interface for Numbat? #536

Open
endigma opened this issue Aug 14, 2024 · 12 comments · May be fixed by #550
Open

Notepad/soulver-like interface for Numbat? #536

endigma opened this issue Aug 14, 2024 · 12 comments · May be fixed by #550

Comments

@endigma
Copy link

endigma commented Aug 14, 2024

Are there any plans to develop a Notepad/soulver-like interface for Numbat? Something like:

I think using numbat as the backend for something like this could be really good, most of these struggle with actually manipulating the units they "support", for example:

image
or
image

I think this could likely be achieved by essentially re-running numbat with 1..n lines of the file provided and adding the output per-line, however there's probably a more clever way to do this. If it's supported, adding something like total or subtotal could also be very useful.

@sharkdp
Copy link
Owner

sharkdp commented Aug 15, 2024

I would love that, too. I mentioned something similar here. Something that comes quite close are the interactive examples in my article about the Numbat type system here. The HTML/JS/CSS for that (here) can probably serve as a good starting point. It feels like it should be possible to build a PoC rather quickly.

@sharkdp
Copy link
Owner

sharkdp commented Aug 15, 2024

In order to run this locally, one needs to build the WASM version of Numbat using

cd numbat-wasm
bash build.sh

And then copy the generated www/pkg/ folder over to the directory mentioned above.

@endigma
Copy link
Author

endigma commented Aug 16, 2024

This looks interesting, what do you think of how to get outputs working per-line of the doc? I'm pretty sure implementing it with a for loop basically feeding lines 1..n of the file into numbat would "work", but I'd really rather see some more sane implementation. What if numbat could have some sort of machine readable output mode where it spits out incremental results with source line numbers?

maybe something like:

input:

let x = 5
x + 4
x + 7

output:

[{line: 1, output: "let x: Scalar = 5"}, { line: 2, output: "9"}, {line: 3, output: "12"}]

(maybe with some more machine readable way of representing assignments, results, conversions etc)

Also, what do you think about impl for stuff like total and subtotal? I'm not super familiar with how numbat works but I assume you're parsing everything into an AST, does whitespace awareness get lost in that process?

@sharkdp
Copy link
Owner

sharkdp commented Aug 21, 2024

This looks interesting, what do you think of how to get outputs working per-line of the doc? I'm pretty sure implementing it with a for loop basically feeding lines 1..n of the file into numbat would "work", but I'd really rather see some more sane implementation. What if numbat could have some sort of machine readable output mode where it spits out incremental results with source line numbers?

So if the input is already split into single statements (usually a single line, but can be more), that would work even today by creating one numbat::Context and then feeding each statement in one by one. We wouldn't have to do inputs 1..1, 1..2, 1..3, etc. because the Context remembers previous statements. So we would feed in statement/line 1 and get an output; feed in statement/line 2, get and output, etc.

[{line: 1, output: "let x: Scalar = 5"}, { line: 2, output: "9"}, {line: 3, output: "12"}]

Something like this might be needed eventually (and wouldn't be too hard to build, I believe), but I think we could start playing with what I suggested above to get an impression if this would work.

Also, what do you think about impl for stuff like total and subtotal? I'm not super familiar with how numbat works but I assume you're parsing everything into an AST, does whitespace awareness get lost in that process?

Do you mean total similar to what figr.app does? that should be doable today?

image

Or total as in similar to what numara.io does?

image

That is something I'd be cautious about implementing. It's not really clear to me if this feature doesn't have any hidden footguns. It feels very much like a "global hidden state" thing. And I'd rather have a language with clear semantics where identifiers don't change their meaning if they are being moved one line below.

@sharkdp
Copy link
Owner

sharkdp commented Aug 21, 2024

Look: I built a first prototype here: #550 (try it online here: https://numbat.dev/editor.html)

@endigma
Copy link
Author

endigma commented Aug 31, 2024

For total I did mean something context-aware like numara, or numi. I think there's definitely some footguns in there but especially for finance stuff it's a super useful feature to have the subtotal and total keywords. Subtotal grabs the total of the previous unbroken lines outputs and total grabs all the subtotals, even if not shown with keyword "subtotal"

What if there was a way to make it clear which statements were being totaled? Maybe something like this? I'm very unfamiliar with numbat as whole so please excuse my naivete if this is nonsensical

let Nights = 10

let total = sum {
    let Accom = $ 120 * Nights
    let Food = $ 200 * Nights 
    ... and so on...
 }
 
 total = ... [Money]

and subtotals with something like

sum {
    sum {
        ...
    }
    sum {
        ...
    }
    sum {
        ...
    }
    any_arbitrary_statement
}
 

@sharkdp
Copy link
Owner

sharkdp commented Sep 6, 2024

To be honest, I'm not convinced. Can you show me some real-world examples?

By the way, the example above with the travel costs could also be written with a custom unit. Let's say we would have

@aliases(nights)
unit night

then we could write it as:

let flights = $ 600

let accomodation = 120 $ per night
let food = 60 $ per night
let spending_money = 50 $ per night

flights + 10 nights × (accomodation + food + spending_money)

@endigma
Copy link
Author

endigma commented Sep 8, 2024

It's useful in pure-finance type calculations where you could do things like:

sum {
	# Funds
	sum {
		main = 798.88 CAD
		joint = 5059.42 CAD
		tfsa = 31439.60 CAD
		rent = 2033.14 CAD
		...
	}

	# Expenses
	sum {
		rent = -1750 CAD * 12
		power = -200 CAD * 6
		food = -500 CAD * 12
		...
	}
}

the sum operator or a context-aware total/subtotal would allow you to use it instead of large (x + y + z + p + q + r) statements that have to be updated every time you add a new item.

in an imperative language, I would write something like

package main

import "fmt"

type Summable interface {
	Sum() float64
}

type (
	Item struct {
		memo   string
		amount float64
	}
	Items []Summable
)

func (item Item) Sum() float64 {
	return item.amount
}

func (items Items) Sum() float64 {
	total := 0.0

	for _, i := range items {
		total += i.Sum()
	}

	return total
}

func main() {
	total := Items{
		Items{
			Item{"main", 798.88},
			Item{"joint", 5958.42},
			Item{"main", 31439.60},
			Item{"main", 2033.14},
		},
		Items{
			Item{"rent", -1750 * 12},
			Item{"power", -200 * 6},
			Item{"food", -500 * 12},
		},
	}.Sum()

	fmt.Println(total)
}

which may inspire a different implementation idea in Numbat

@sharkdp
Copy link
Owner

sharkdp commented Sep 11, 2024

I realize that this could be slightly more ergonomic, but you can do similar things in Numbat. For example:

struct Item {
  what: String,
  amount: Money
}

fn fund(what, amount) = Item { what: what, amount: amount }
fn expense(what, amount) = Item { what: what, amount: -amount }

let funds = [
    fund("main", 798.88 CAD),
    fund("joint", 5059.42 CAD),
    fund("tfsa", 31439.60 CAD),
    fund("rent", 2033.14 CAD),
]

let expenses = [
    expense("rent", 1750 CAD * 12),
    expense("power", 200 CAD * 6),
    expense("food", 500 CAD * 12),
]


fn amount(i: Item) = i.amount
fn total(items) = sum(map(amount, items))

print("Funds:    {total(funds):>10.0}")
print("Expenses: {total(expenses):>10.0}")
print("Total:    {total(concat(funds, expenses)):>10.0}")

which prints:

Funds:         39331 CAD
Expenses:     -28200 CAD
Total:         11131 CAD

@sharkdp sharkdp linked a pull request Sep 11, 2024 that will close this issue
2 tasks
@endigma
Copy link
Author

endigma commented Sep 12, 2024

thats cool actually, i didn't realize numbat had structs, i should probably rtfm before posting ideas lol

@ValentinLeTallec
Copy link
Contributor

Would it be possible to also integrate it to the CLI ?
What I have in mind is a pseudo "formatter" that would add special comments (#>>> in my example) with the results.

Advantage of making it a formatter :

  • Ease of integration with existing editors
  • With format on save set on the editor, one can work in their favorite editor, save and get instant results
  • The results are actually written in the file, so one can directly access them anywhere (for example, even if they are synced on a phone)
  • Depends on the editor, but I imagine one could have a numbat code block in a markdown file and have it formatted that way to mix notes and calculus

Before formatting

8 km / (1 h + 25 min)

atan2(30 cm, 1 m) -> deg
#>>>  "An old result that will be overwritten" [String]

let ω = 2π c / 660 nm
ℏ ω -> eV

fn breaking_distance(v) = v t_reaction + v² / 2 µ g0
  where t_reaction = 1 s # driver reaction time
    and µ = 0.7          # coefficient of friction

breaking_distance(50 km/h) -> m

After formatting

8 km / (1 h + 25 min)
#>>>  5.64706 km/h [Velocity]

atan2(30 cm, 1 m) -> deg
#>>>  16.6992°

let ω = 2π c / 660 nm
ℏ ω -> eV
#>>> 1.87855 eV [Energy or Torque]

fn breaking_distance(v) = v t_reaction + v² / 2 µ g0
  where t_reaction = 1 s # driver reaction time
    and µ = 0.7          # coefficient of friction

breaking_distance(50 km/h) -> m
#>>>  27.9392 m [Length] 

@sharkdp
Copy link
Owner

sharkdp commented Oct 8, 2024

New preview version is here: https://numbat.dev/editor.html (thanks @Goju-Ryu)

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

Successfully merging a pull request may close this issue.

3 participants