The following blog was written by Russell for our blog: https://www.yoyogames.com/blog/506/gml-consistency-in-version-2-2-2
INTRO
GML as a language has slowly grown over the last 20 years, and during that time we have added features and addressed inadequacies within the language. Most recently, as of the GameMaker Studio 2 version 2.2.2 release we have been addressing consistency issues between the VM and YYC compilers. We have particularly addressed issues of type-conversion consistency at runtime.
To this end, we have been augmenting our internal test suite, making it easier to create tests, and revised the suite to ensure that it shines lights on some of the darkest corners of the language where users fear to tread. This was most illuminating and helped to uncover some glaring issues within the VM and YYC compilers, and hopefully, everyone will see improvements in compatibility between these versions in the 2.2.2 release. This test suite is run every day against our latest internal builds and has been catching regressions and mistakes since we set it all up. Even as new bugs come in, we have been adding to the suite and ensuring that these issues are fixed as a priority.
CHANGES
The main thrust of this blog post was to draw your attention to changes that may affect how your code executes at runtime. Previously, the runtime was quite inconsistent in how it handled variables which were being used as a number but were actually strings - some functions internally converted them to numbers or just defaulted to the value being 0, while some errored, and others just silently continued with incorrect values.
We have addressed this by revising the runtime to make string-to-number type-conversion consistent and introducing some basic rules:
This now makes the runtime more consistent (and predictable) and along with our testing suite gives a good basis for addressing other stability and language issues and additions over the coming months.
WRAP UP
I have had discussions with GMS2 developers and several questions have come up that I think stem from confusion around how operations are handled at runtime.
One common observation was that this now makes the + operator non-commutative since 1 + “2” does not equal “2” + 1. Well, the simple answer here is that these are not the same operations, as 1 + “2” is an integer addition with another number (so the runtime requests the string to be converted) while “2” + 1 is a string addition (which is actually a concatenation operation) with a number, which the concatenation operator does not allow and so an error is generated. This is because the type of operation on a binary operator is dictated by the type of the left-hand element in the operation. This has always been the case in GameMaker, and it is not a breaking change as of 2.2.2. This is true for all operators in GameMaker.
I should call out at this time that there is a little-known quirk in GameMaker that the * operator performs a little differently when an integer is multiplied by a string. Then, the string is concatenated to itself times the value of the integer - so 3 * “9” results in “999”. This behaviour has been part of the language for a long time (at least from GM7, which is the first version I was involved in).
Another observation would be that the compiler will also optimise any constant expressions that it can handle, but it will leave constant string conversion to numbers until runtime, as this change for 2.2.2 was focused on runtime conversions and not compile-time. We have also left some string expressions as errors at compile time, for example, constant strings. So ~”6” will generate an error at compile time, as trying to do a bitwise negate with a string is incorrect and should be changed by the user, but at runtime if it was a variable at runtime, then it would successfully convert it, as the user may not realize that the type of the variable had been changed earlier elsewhere in their code. Now, obviously, this conversion at runtime is more expensive when it happens, so I would advise you to profile your code in the debugger to make sure it is not taking more time than it should, and you can also check types of variables at runtime using the typeof() function see: https://docs2.yoyogames.com/source/_build/3_scripting/3_gml_overview/checking_data_types/typeof.html
We look forward to growing GML more with our community in the future. If you want to discuss these changes, feel free to join the 2.2.2 beta discussions here.
Thank you, Game Makers!
INTRO
GML as a language has slowly grown over the last 20 years, and during that time we have added features and addressed inadequacies within the language. Most recently, as of the GameMaker Studio 2 version 2.2.2 release we have been addressing consistency issues between the VM and YYC compilers. We have particularly addressed issues of type-conversion consistency at runtime.
To this end, we have been augmenting our internal test suite, making it easier to create tests, and revised the suite to ensure that it shines lights on some of the darkest corners of the language where users fear to tread. This was most illuminating and helped to uncover some glaring issues within the VM and YYC compilers, and hopefully, everyone will see improvements in compatibility between these versions in the 2.2.2 release. This test suite is run every day against our latest internal builds and has been catching regressions and mistakes since we set it all up. Even as new bugs come in, we have been adding to the suite and ensuring that these issues are fixed as a priority.
CHANGES
The main thrust of this blog post was to draw your attention to changes that may affect how your code executes at runtime. Previously, the runtime was quite inconsistent in how it handled variables which were being used as a number but were actually strings - some functions internally converted them to numbers or just defaulted to the value being 0, while some errored, and others just silently continued with incorrect values.
We have addressed this by revising the runtime to make string-to-number type-conversion consistent and introducing some basic rules:
- A string will be converted to a number at runtime IF it starts with a valid number i.e. begins with a digit or ‘+’ or ‘-‘. The number is then parsed up until the last non-number character. NOTE: number conversion is treated as floating point number conversions and follow the rules for the C runtime function “atof”, which is documented as “A valid floating point number for atof using the "C" locale is formed by an optional sign character (+ or -), followed by a sequence of digits, optionally containing a decimal-point character (.), optionally followed by an exponent part (an e or E character followed by an optional sign and a sequence of digits).”
- Any non-number characters after the number will be ignored.
- Any string that cannot be converted will cause an error to be generated, except in the case where a simple Equals or Not Equals comparison is being done (e.g. “Hello” != 0 would be true).
This now makes the runtime more consistent (and predictable) and along with our testing suite gives a good basis for addressing other stability and language issues and additions over the coming months.
WRAP UP
I have had discussions with GMS2 developers and several questions have come up that I think stem from confusion around how operations are handled at runtime.
One common observation was that this now makes the + operator non-commutative since 1 + “2” does not equal “2” + 1. Well, the simple answer here is that these are not the same operations, as 1 + “2” is an integer addition with another number (so the runtime requests the string to be converted) while “2” + 1 is a string addition (which is actually a concatenation operation) with a number, which the concatenation operator does not allow and so an error is generated. This is because the type of operation on a binary operator is dictated by the type of the left-hand element in the operation. This has always been the case in GameMaker, and it is not a breaking change as of 2.2.2. This is true for all operators in GameMaker.
I should call out at this time that there is a little-known quirk in GameMaker that the * operator performs a little differently when an integer is multiplied by a string. Then, the string is concatenated to itself times the value of the integer - so 3 * “9” results in “999”. This behaviour has been part of the language for a long time (at least from GM7, which is the first version I was involved in).
Another observation would be that the compiler will also optimise any constant expressions that it can handle, but it will leave constant string conversion to numbers until runtime, as this change for 2.2.2 was focused on runtime conversions and not compile-time. We have also left some string expressions as errors at compile time, for example, constant strings. So ~”6” will generate an error at compile time, as trying to do a bitwise negate with a string is incorrect and should be changed by the user, but at runtime if it was a variable at runtime, then it would successfully convert it, as the user may not realize that the type of the variable had been changed earlier elsewhere in their code. Now, obviously, this conversion at runtime is more expensive when it happens, so I would advise you to profile your code in the debugger to make sure it is not taking more time than it should, and you can also check types of variables at runtime using the typeof() function see: https://docs2.yoyogames.com/source/_build/3_scripting/3_gml_overview/checking_data_types/typeof.html
We look forward to growing GML more with our community in the future. If you want to discuss these changes, feel free to join the 2.2.2 beta discussions here.
Thank you, Game Makers!