refaktor / rye

homoiconic dynamic programming language with some new ideas
https://ryelang.org
Apache License 2.0
406 stars 21 forks source link

Unexpected mathematical operator priority #99

Open stefanb opened 8 months ago

stefanb commented 8 months ago

Operators with same mathematical priority seem to be evaluated from the right:

×> 1.0 / 10.0 * 100.0
[Decimal: 0.001000]
×> 1.0 / 10.0 / 100.0
[Decimal: 10.000000]

Either this should be clearly documented or fixed or prevented.

refaktor commented 8 months ago

Thank you for the feedback. Good observation. It will get documented in the Meet Rye where we talk about op and pipe words.

It's not a bug (but might still change if we find a consistent way), but the fact that +-/* are just op-words (without the need to have "." in front) and behave the same as other op-words. Words that are bound to a function can be invoked as op-words in which case they take first argument from the left. Op-words take the first value they can get on the left, while pipe words wait so that everything on the left is evaluated and then take the value.

quick intro to op-words

concat "Fizz" "Buzz" ; function invoked by normal word in polish notation
"Fizz" .concat "Buzz" ; function invoked as op-word
"Fizz" |concat "Buzz" ; function invoked as pipe-word
; all three return "FizzBuzz" 

One letter operator characters are automatically loaded as op-words, where their real spelling is _+ for +:

_+ 33 22   ; normal word
33 + 22     ; op-word
33 |+ 22   ; pipe-word

Other than that (loading it as op-word) that there is no difference between + or any other word/function.

Rebol in the name of uniformity also doesn't have usual mathematical precedence rules, but it has them left to right, which is better than what Rye has (but there operators are special case, while in Rye they aren't). Lisps only have prefix operands, forths postfix ... we are moving in this family of languages.

The difference between op-and pipe word is maybe best demonstrated by this:

222 + 333 .print |print
; prints: 
; 333
; 555

It behaves the same for + and |+ or and | ... which is uniform, but not the best way to write math expressions.

That's why I've already made a test implementation of "Math dialect" (which at least Rebol never had, not sure for lisps). It follows the normal math precedence rules, parenthesis (which Rye2 so far doesn't yet support, but will probably need to at some point ), and I will try to add even more math friendly syntax support, for combining with other regular rye functions. Small demo here: https://www.reddit.com/r/ryelang/comments/18owc2e/new_math_dialect/

The default op-word / pipe-word behaviour is also not totally sealed yet ... we will see about it. Math dialect will also be exploration about that.

refaktor commented 8 months ago

I will leave this issue open until the behavior is properly documented or changed in other way. Thanks!

refaktor commented 7 months ago

Just adding food for thought. I was making an example today. Where I first wanted to multiply by -1 and then add up and this behaviour bit me.

{ -2 -7 1 5 }  .reduce 'acc { * -1 + acc }  

So it would have to be (which is frankly ugly):

{ -2 -7 1 5 }  .reduce 'acc { * -1 |+ acc }  

We could say that operators aren't op-words but pipe-words to solve case as above, but I suspect pipe words have more specific bahaviour and woud lead to weird results given the code. Here current behavious to me makes sense:

inc 1 * 10    ; 11 
inc 1 |* 10  ; 20 

Third way would be that operators behave differently, but does this mean we have a third type/behaviour for words (like op and pipe now), or that all op-words beahve diffrently. I have to experiment.

refaktor commented 5 months ago

Math dialect works quite well and it should be the preferred way to enter any non-trivial math expressions. More info here: https://www.reddit.com/r/ryelang/comments/1bgws3z/ryes_math_dialect/

Currently I found no other evaluation strategy for functions that would make more sense ( be easier to understand, use and think about) than current op-word and pipe-words so the behavior for now will stay the same and be documented. Operators behave like other op-words (functions with . on the left).