Len-operator has a wrong precedence. (continued)

BlitzMax Forums/BlitzMax Programming/Len-operator has a wrong precedence. (continued)

Fabian.(Posted 2006) [#1]
You should have read the previous thread about this topic first to understand this one.

Hi Mark, it's me again - here's just one quote:
Built-in 'bracketless' functions like this are dumb and I shouldn't have put them in in the first place.
Are you sure about that? Just remember the time when you decided to implement this as operator. I think you had three alternatives:

1.: Implement this as plain and normal function. This would slow down the resulting applications a lot, since call's to this function would be very often and it would make no sense to perform a stack intensive function call just to find out the length of a string or array object. Then there is also a language syntactical problem: How should this function be declared? Which parameter should it accept - a string or an array? It should accept both, however, BlitzMax doesn't support function overloading, so that you couldn't implement it for both, strings and arrays.

2.: Implement it as builtin-function, which always needs brackets. This would be quite fast (compared with a normal function) and syntactical possible. However, it would confuse the programmer: How can you differ between a builtin-function (which is a kind of operator) and a normal function? It is important to always be aware of whether you're using an operator or a function, since they have many different properties and attributes assuming execution speed, stack and register usage, variable types (you can't store operators in variables, functions however, can be stored). The syntax used to execute this operator would be equivalent to the syntax used to call a function. This would cause confusion for the BlitzMax users, since they couldn't definitely decide whether they're using a builtin-function or a real function. It would also be a lack of consistency, since all other operators don't need brackets.

3.: Implement it as plain and normal operator - without brackets. This would be as fast as the second alternative, since it is an operator as well. It would also be syntactically correct, since BlitzMax already has unary operators (negation operator and New-operator) and you would implement it the same way as you did with the others (which would give the language more consistency). Then you have two alternatives regarding the precedence of that operator:
a.: Give it the same precedence as the New-operator. This would result in a higher precedence than the member access operator, which would cause the programmer to also put brackets around the operator's parameter when accessing the length of an array or string inside an object and would have the same disadvantages like the alternative number two: The BlitzMax user wouldn't be aware of whether using an operator or a function, which could result in strange compiling bugs.
b.: Give it the same precedence as the negation operator. Then it would have a lower precedence than the member access operator, but a higher precedence than the mathematical operators. The user could easily access the length of an array or string stored in an object (thanks to lower precedence than member access operator), then the user also could do some calculations with the resulting length (thanks to higher precedence than mathematical operators). So the language could become consistent, easy to use and easy to understand.

Which alternative did you choose? You chose number 3b, which was the best and easiest thing you could do. But what did you change between version 1.20 and 1.22? You just changed it to alternative number 3a, which is resulting in a strange mix: Len is now neither an operator with easy to use precedence (which all the other operators have) nor a builtin-function nor a real function. It is an operator with an unreasonable precedence, causing people to put brackets in their code and making them think it was a function, which needed brackets (if I just quote the topic title of a thread in the programming forum: "Len is now a FUNCTION and not an operator").

What we now have is inconsistency. Not just because the precedence makes people think it was a function, also because the current BlitzMax versions (1.22 and 1.24) don't fit to all previous versions - code which compiled fine with 1.20 and the versions before doesn't do so anymore in version 1.22 and 1.24: BlitzMax lost backward compatibilty. However, backward compatibility is important if you want to develop bigger projects in BlitzMax - just imagine you developed most of your code in version 1.20 and just waited with releasing because you hoped some little bugs will be fixed in version 1.22; now you notice, the code doesn't compile anymore with version 1.22, however, if you compile with version 1.20 you'll still have these bugs and the programme you developed doesn't work the way you want. However, BlitzMax could only become a famous language if there were some bigger, successfull and famous applications programmed in it. But how do you want to make people develop professional programmes in BlitzMax if you don't support downward compatibility? Just compare with Java: have you ever heard of an operator in Java which has changed it's precedence? I didn't - however, this happened in BlitzMax, which is not a good omen for the future of BlitzMax.

If you say: "Now I can't change it back, because I would hurt backward compatibilities again, so it has to stay like it is now in version 1.24." Just think: How would an expression look like which can compile in version 1.22 and 1.24 and which can't compile in version 1.20? It would need to use the member access operator on a result of the Len-operator; however, the Len-operator returns an integer, but the member access operator needs a module, scope, type or object as parameter. So you won't find any code which compiles with 1.22 and 1.24, but doesn't with 1.20. You could easily say, changing the precedence was a bug and you'll change it back to the correct precedence, which would be the easiest way to solve this problem, also since you wouldn't have to change the documentation ;-)


marksibly(Posted 2006) [#2]
Hi,

To me, the point is that Len, Chr, Asc etc *should* be normal functions, and the compiler should inline them so that they are as fast as anything builtin.

The fact the compiler can't/doesn't inline them is a crap excuse to make the language more complex and messier - but that's what I decided to do at the time. It was a mistake.

I don't really want to change this back to the 'better but still crappy' 1.18 precedence because changes to the predence of things has been proven to have nasty side effects. And 99.99% of users have been quite happy with things since 1.22 so...

As for why I changed precedence in 1.20 at all, that too was a huge mistake! That was done to 'fix' the precdence of Chr, but I really just should have said 'that's the way it is - live with it'. Frankly, I don't want to repeat the exercise.

My advice would be to use '.length' instead of Len, and treat Asc, Chr etc as if they were functions - ie: always use brackets. Any potential Max2 would certainly implement these as functions anyway, so think of it as future proofing.

You are of course free to disagree with any of this, but please don't post this as a bug report again. The manual, on the other hand, does indeed need fixing...


Fabian.(Posted 2007) [#3]
Hi,
sorry for bringing this old topic up again, but after I lately read your post again, I have to say that I'm not quite sure anymore about what is the major reason for you to keep this precedence.

You wrote:
Built-in 'bracketless' functions like this are dumb and I shouldn't have put them in in the first place.
And in this point I totally agree with you, like you said: "Any potential Max2 would certainly implement these as functions anyway", but this would be Max2, which, if it ever exists, would be a new language. However, I just would like to keep code running in Max1. And since in Max1 Len has already been implemented as operator, it is now not possible anymore to change this. It would just result in a bad mix. Though it would be better to have Len implemented as function (I agree with you), it is already implemented as operator, so we should stay with it.
I don't really want to change this back to the 'better but still crappy' 1.18 precedence
The fact that Len is an operator is crappy, okay I agree - however, the precedence which this operator had wasn't crappy at all, since it was the best one I could imagine for this operator. The previous precedence made perfect sense to me, because the code you wrote was shorter and easier to read; just have a look at this:
Local N = Len anobject.array 'get the length of the array in the object
If you look at the source code you'ld normally say that things which are closer together should be compiled first, and only thereafter the compiler takes account of phrases which are more apart. And this was exactly what the compiler did previously: The member access operator was executed first.
The current precedence tells the compiler to compile the Len-operator first, though it is seperated from its parameter with a space. Of course this isn't the most important thing, however, it shows that the previous precedence was more logically and was resulting in easier to read code.
To me, the point is that Len, Chr, Asc etc *should* be normal functions, and the compiler should inline them so that they are as fast as anything builtin.
I also think so, however, the reality is that they are not normal functions, and if they were normal functions, the compiler couldn't inline them.
Changing the precedence like you did, doesn't change these facts at all. Changing these facts would need so many changes, that in the end, you'd have a completely new language (Max2).
Frankly, I don't want to repeat the exercise
Everything I know about how to write a compiler tells me that changing the precedence just means changing a special number or the order of some statements.
Or did you want to say that somebody could cause a problem by saying: "My code which worked in version 1.24 doesn't work anymore in 1.26!"?
You could simply reply to this person: "The documentation says clearly that the code you wrote isn't correct BlitzMax code. The fact that the code doesn't work anymore in 1.26 is just correct. However, the fact that it worked at all in 1.24 was a bug caused by a temporary wrong precedence"
I don't really want to change this back ... because changes to the predence of things has been proven to have nasty side effects
I thought the last section of my previous post should explain this.
Okay, it just names the Len-operator - however, the Asc-operator has equal parameters and return value like the Len-operator, so the same rules which apply on Len, also apply on Asc.
The only difference is with the Chr-operator, but you'll notice that there's only one special case where this appears to have effects, but as said above someone writing code which is effected by this change doesn't know about the manual, so this person would have a good reason for changing the code and not posting bug reports.
As for why I changed precedence in 1.20 at all, that too was a huge mistake!
...
The manual, on the other hand, does indeed need fixing...
So why do you still stay at the opinion of keeping this mistake by just calling it the correct behaviour next version (by changing the documentation to fit the precedence)?
And 99.99% of users have been quite happy with things since 1.22 so...
I haven't heard about anyone saying: "Wow, we got a new precedence, thank you."
By the way: Though changing the precedence is an important thing, it isn't mentioned in the changelog at all; I think this was the reason for the fact that 99.99% of the users didn't notice that the precedence changed. Of course many people were happy about 1.22 (I was happy as well), since many little bugs from previous versions were fixed (for example casting a negative Long to Int). --- I use this thread to say it once more: Thanks for these updates, they're great, since fixing lots of little bugs. --- If it would really be true that 99.99% of the users are happy with the precedence change, or prefer the new precedence to the old one, they either haven't read the documentation or just don't care about it, or they don't know that the new precedence is (at least regarding the Len and Asc operators) just a restriction which doesn't allow any knew phrases which were not allowed in the previous one.
I think I agree with you that the old precedence was better (you said it yourself...)

I'm now a bit disappointed, since I always wrote all my code like it was documented and suggested by the manual and have now to notice that a big part of my code won't compile anymore and will be declared has wrong next version (as soon as you "fix" the manual), just because some other users, who don't care about the manual, wanted a part of wrong code to compile like it were correct.

The cases, where the new precedence allows something which isn't allowed in the old one, are extremely rare compared to that big amount of code which doesn't work anymore since the newer versions (and I'm not the only one who wrote code which doesn't compile since 1.22).
I guess that if you change this back to the old precedence, 99% of the potential possible code and 100% of the actually existing code will still work in 1.26.
And even if somebody finds a piece of code that doesn't work in 1.26 anymore you could simple tell that the code is bugged and was never meant to compile at all - only reason why it compiled was a bug in two previous versions.
My advice would be to use '.length' instead of Len,
Thanks for the advice, I'll do this when I start a new project, though it takes longer to type. However, the problem to me is that 75% of my already existing code files don't compile with the 1.22 and 1.24 bcc; changing them to work would require more time than working around the other little bugs in 1.20 (for example the casting bug from negative Long to Int can be solved by writing an own cast-function, works, it's just annoying), so I have to stay at 1.20 which will result in a problem for me as soon as I discover a bigger bug in 1.20.
Compared with the fact that these 75% were always and are still documented to be correct this is absolutely annoying.

Since you changed such a basic behaviour of the language like the operator precedence is, it looked to me like you wanted to "reinvent the wheel"... it just reminded me of what I read in a different thread with a completely different topic, just the same statement from a different user:
As a customer I think I feel let down by you constantly reinventing the wheel and faffing around
I just don't like the idea to change the precedence at all after the compiler was initially released. Therefore I want to stay with the previous initial precedence. I hope I could describe you my the problem - it's just that I still don't understand why you don't want to change it back, it would be nice to hear why it is that way.