sharkdp / insect

High precision scientific calculator with support for physical units
https://numbat.dev/
MIT License
3.18k stars 126 forks source link

Support converting quantity to base units #374

Closed triallax closed 1 year ago

triallax commented 1 year ago

Fixes #184.

To be honest, this is very much not how I want this to be implemented. I was thinking about adding something like Base to the Expression datatype, but since base in -> base isn't a unit per se but a part of the language's syntax, it can't be cleanly added to Expression currently. This also introduces the issue of how to handle conversion to a base variable:

>>> base = 2m

  base = 2 m

>>> 5 V -> base

  5 V ➞ base

   = 5000 m²·g/(s³·A)

If we choose to use this (terrible IMO) implementation, should base be allowed as a variable name? If so, how do we allow conversions to it? With something like -> (base) perhaps? (the parser, or maybe something else, currently seems to fold expressions in parentheses, so this will also need some changes there)

Alternatively, we may want to special-case -> base in the parser into something like ConvertToBase.

I'd love to hear your thoughts about how to best implement this (if at all) @sharkdp.

sharkdp commented 1 year ago

Thank you for looking into this!

To be honest, this is very much not how I want this to be implemented. I was thinking about adding something like Base to the Expression datatype, but since base in -> base isn't a unit per se but a part of the language's syntax, it can't be cleanly added to Expression currently. This also introduces the issue of how to handle conversion to a base variable:

Ok, so I guess it makes sense to discuss the change to the language first.

  1. The "hacky" way you proposed here: do not properly support this in the parser, just special-case it in the interpreter.
  2. Add a new reserved keyword base, disallow usage of base as a variable name. Then, modify the parser such that expr -> base is not parsed as a BinOp expression, but rather as a unary operator acting on expr — similar to what we do for factorials or negate-expressions. We could then add a new expression variant ConvertToBase Expression that would be handled separately in the interpreter. And I just realized that you proposed the exact same thing in the original post above.. even using the same name.
  3. Find a completely different syntax for this. I'm not 100% sold on the -> base syntax anyway, and would appreciate if we could think about alternatives. Maybe something that avoids the drawbacks of 1. and 2.

I agree with you that (1) does not seem like the best option here, even if the base variable problem would not be there (I don't think it's a huge deal). I would suggest we explore (3) for a bit and otherwise look into (2).

triallax commented 1 year ago

I agree with you that (1) does not seem like the best option here, even if the base variable problem would not be there (I don't think it's a huge deal). I would suggest we explore (3) for a bit and otherwise look into (2).

Makes good sense to me. The only alternate syntax that came to my mind is :base or :to-base, but I don't think that fits here because it's not really a command.

Qyriad commented 1 year ago

Perhaps a bit more "out there", but at least avoids special cases: you could allow the -> syntax to take a function on the righthand side, which passes the result as an argument to the function, and then declare a built-in function (base() or similar) that decomposes a value's units into its base units. This would also allow you to do things like 300 kelvin -> toCelsius, which would be equivalent to toCelsius(300 kelvin).

triallax commented 1 year ago

@Qyriad interesting idea, but it may be a bit too magical for Insect's design. If @sharkdp doesn't have a problem with it though, it's fine by me.

sharkdp commented 1 year ago

So in the rewrite of Insect that I am currently working on (let's see if that project is ever finished), I added support for this Mathematica-style syntax that I like very much when doing numerical calculations: expr // f is equivalent to f(expr). The // operator has a very low precedence (similar to ->), so you can write e.g. 20 + 80 // sqrt and it's equivalent to sqrt(20 + 80). This is super useful in a REPL, because you can just press up-arrow to get the previous computation and just append a function call with // ….

Combining this with the idea by @Qyriad, we could then write expr // base or similar and it would be in line with other function calls, but also easy to add to the end of a computation.

triallax commented 1 year ago

So in the rewrite of Insect that I am currently working on (let's see if that project is ever finished), I added support for this Mathematica-style syntax that I like very much when doing numerical calculations: expr // f is equivalent to f(expr). The // operator has a very low precedence (similar to ->), so you can write e.g. 20 + 80 // sqrt and it's equivalent to sqrt(20 + 80). This is super useful in a REPL, because you can just press up-arrow to get the previous computation and just append a function call with // ….

I love this idea! The specific use case you mention can be currently satisfied using _/ans (e.g. f(_)), but I think the idea is cool regardless. Maybe we should open a separate issue for this?

Combining this with the idea by @Qyriad, we could then write expr // base or similar and it would be in line with other function calls, but also easy to add to the end of a computation.

I'm somewhat more lukewarm about this one, because it adds a new and separate syntax for unit conversions. I'd prefer the ConvertToBase approach instead, because at least it's consistent with the current unit conversion syntax.

Edit: now that I think about it, this criticism applies to my :base/:to-base suggestion too, and your whole (3) option...

sharkdp commented 1 year ago

I love this idea! The specific use case you mention can be currently satisfied using _/ans (e.g. f(_)), but I think the idea is cool regardless

Yes, _ often helps. But // is also useful in cases where you are just writing a single computation in a single line. Because you are writing it in the order that the operations are applied. It's the same as # in PureScript :smile:.