Open endigma opened 2 months ago
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.
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.
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?
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
andsubtotal
? 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?
Or total
as in similar to what numara.io does?
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.
Look: I built a first prototype here: #550 (try it online here: https://numbat.dev/editor.html)
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
}
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)
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
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
thats cool actually, i didn't realize numbat had structs, i should probably rtfm before posting ideas lol
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 :
format on save
set on the editor, one can work in their favorite editor, save and get instant resultsBefore 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]
New preview version is here: https://numbat.dev/editor.html (thanks @Goju-Ryu)
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:
or
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
orsubtotal
could also be very useful.