robpike / ivy

ivy, an APL-like calculator
Other
1.33k stars 103 forks source link

Division vs fraction ambiguity #120

Closed kpym closed 1 year ago

kpym commented 1 year ago

I understand that / has (at least) three different meanings:

The problems starts when we mix them:

I can guess that the scanner works left to right and if it finds a fraction, it grabs it. IMO this creates some inconsistency between fractions and divisions.

My first idea was to simply abandon the "fraction meaning" and to consider only divisions, where a fraction is just the result of a division of integers. But in this case 1 2/3 will be (1 2)/3 == (1/3) (2/3) and not 1 (2/3). So to write vectors with fractions will be painful.

I don't know if it is possible to scan right to left for fractions, or may be to give precedence to division over fraction where the ambiguity is present, like in expressions 1/2/3/4/5... ?

robpike commented 1 year ago

It is indeed the scanner. 1/2 is tokenized as a single rational value. Yes, it's clumsy sometimes, but spaces help. Not sure there's really much that can be done, and spaces are easy to use and add clarity. There is no set goal here of minimum keystrokes, unlike the culture of APL.

kpym commented 1 year ago

There is no set goal here of minimum keystrokes, unlike the culture of APL.

If this is the case, we don't need the fraction separator because we can write vectors like this 1 (2/3) 4 ... and all ambiguity is removed. A fraction will just be the result of a division and all precedence rules will be respected.

After thinking about it last night, my proposal is more of a compromise between the current behavior (scanning for fractions first and avoiding parentheses in vectors) and the more lengthy to type, but unambiguous, case without fraction separators.

IMO, we can consider / as a fraction separator only in a vector of length greater than 1, not a simple number. In all other cases consider it as a division.

Examples :

This is a simple idea and there are probably some corner cases, or the way the scanner works, that make this change impossible. And of course there is the backwards compatibility. For all these reasons I can consider my suggestion as a purely theoretical proposal :)

Yes, it's clumsy sometimes, but spaces help.

Oh, I didn't know that 1 2/3 is not the same as 1 2/ 3. Is this documented somewhere?

Another possibility might be just to limit fractions to \d+/\d+[^/] ? In this way in ambiguous expressions like 1/2/3/4 there will be no fraction separators, only divisions, and putting spaces inside will not change the meaning.

robpike commented 1 year ago

It's documented in the help, or go doc:

As a special but important case, note that 1/3, with no intervening spaces, is a single rational number, not the expression 1 divided by 3. This can affect precedence: 3/64 is 2 while 3 / 64 is 1/8 since the spacing turns the / into a division operator. Use parentheses or spaces to disambiguate: 3/(64) or 3 /64.

kpym commented 1 year ago

It's documented in the help, or go doc

Thank you, I had missed this point.

I'm closing this issue (which is not an issue), but one last thought: maybe in cases like 1/2/3 there should be a warning "ambiguous expression: add spaces to clarify what is a fraction and what is a division", because I don't see in the documentation why it should be 1/2 / 3 and not 1 / 2/3. It seems to be a simple consequence of the way the scanner works (from left to right).

robpike commented 1 year ago

One easy possibility is to add a config option to disable aggressive scanning of fractions, as that behavior is only important when one is doing rationals, which although interesting is unusual for most purposes.

kpym commented 1 year ago

Thanks for considering this !

EDIT: Oh I see, the comma is a normal binary operator and it is evaluated before on the right and after on the left. That makes sense.

kpym commented 1 year ago

It looks like there is a bad behaviour now with scandivision 1

1,2/3,4
1 2/3 1/2

and if we copy/paste the result we get

1 2/3 1/2
2/3 4

Thus, the string representation of a vector does not produce the vector itself. This makes me think that my first proposal to keep scanning for fractions inside vectors is the right one.

kpym commented 1 year ago

Perhaps scandivision can have two levels ?

)scandivision 0 # (default) always scan for fractions
)scandivision 1 # scan for fractions only inside vectors with length > 1
)scandivision 2 # never scan for fractions
kpym commented 1 year ago

Another possibility is with the current )scandivision 1 to change the string repsresentation of the fractions to be always enclosed in parenthesis.

1 2/3 4
(1/3) (1/2)

which makes sense, since there is no more syntax for fractions, except as a result of division.

I'm very chatty, sorry. I'll stop now, I promise.

robpike commented 1 year ago

I don't like where this is going. I'm going to roll it back and rethink.