Describe the project you are working on:
A game
Describe the problem or limitation you are having in your project:
godotengine/godot#43711 β Newbies are often confused about this and get results that are not what they expected.
print(5 / 2) # 2
print(5.0 / 2) # 2.5
Describe the feature / enhancement and how it helps to overcome the problem or limitation:
I'm not a Python fanatic, but it seems to me that in this case GDScript should be consistent with it:
$ python3 -q
>>> 5 / 2
2.5
>>> 5 // 2
2
Arguments for:
5 / 2 == 2.5 (JavaScript, PHP, etc.).// comments_Main argument: current behavior is bug prone._
If you have an x / y expression, you cannot tell which division will be performed: real or integer. To guarantee real you have to do float(x) / y. To guarantee integer, you have to do int(x / y). See comment.
Python once faced the same problem because is also a dynamically typed language. Version 3 has fixed this issue. See PEP 238.
See also these comments: one, two.
_In addition, this change does not break compatibility as much as it might seem at first glance._ See comment for details.
Describe how your proposal will work, with code, pseudocode, mockups, and/or diagrams:
See above.
If this enhancement will not be used often, can it be worked around with a few lines of script?:
No.
Is there a reason why this should be core and not an add-on in the asset library?:
Yes.
Confusion can be taught out of a person as they get more familiar with the tools they use. On the other hand scripting languages oft-times have a very inconsistent type coercion to achieve that "newb-friendliness" with varying degrees of success, mostly the type of success that backfires a lot. GDScript is clear of that for the most part, so I'd prefer it stays this way.
Also, Python is not the only language that people come from, so I don't see why that would matter.
I generally agree with you, but not in this particular case. In other (probably even most) other languages, the / operator behaves differently, regardless of whether it has // (or div) operator. So it's not just about those coming from Python. I also often say that GDScript is not Python, but this comparison cannot be avoided, since these languages ββare similar in syntax.
And besides, the problem is not only newbies not reading the documentation. The problem is that one / is acting as two different division operators. Let's be guided by objective facts, and not subjective love for one or another programming language.
The problem is that one
/is acting as two different division operators.
It is not though. It just doesn't coerce the type of the return value unless you give it a clear indication that you need to do that. It's as straightforward as it gets, but maybe not obvious to a person that doesn't understand data types yet.
In other (probably even most) other languages, the
/operator behaves differently, regardless of whether it has//(ordiv) operator.
In dynamic languages, maybe. And granted, we have a somewhat dynamic language in GDScript here. But that doesn't mean we have to go into magical behavior to mirror classic maths. I'd prefer we stick to the rules of more strict languages so we have less chances to shoot ourselves in a foot.
Also, shaders would still require you to keep this nuance in mind as they have stricter types.
But that doesn't mean we have to go into magical behavior to mirror classic maths.
<int|float> / <int|float> == <real division>
<int> // <int> == <int division>
If I need mathematical division, I always use / (regardless of the types of the operands). If I need integer division, I use //.
I don't see anything "magical". This works well in most dynamic languages and static languages too (for example, in Pascal).
Also, shaders would still require you to keep this nuance in mind as they have stricter types.
Shaders are irrelevant to this issue. They are written in a completely different language, not GDScript.
I don't see a point in adding // as you can very easily force a specific type onto them.. need result to be int? just do int(Arguments) and it'll spit an int
<float> / <float> == <real division>
<float> / <int> == <real division>
<int> / <float> == <real division>
<int> / <int> == <int division> # Aaaaaaaaa!
# And you have to do:
float(<int>) / <int> == <real division>
# ----------------- vs -----------------
<float> / <float> == <real division>
<float> / <int> == <real division>
<int> / <float> == <real division>
<int> / <int> == <real division>
<int> // <int> == <int division>
Although I know about this trait in GDScript, I sometimes forget about it and when debugging I am surprised that the following code does not work:
ceil(INT_EXPR / 2)
# And this is correct:
ceil(INT_EXPR / 2.0)
It is better to explicitly use another operator when you need exactly integer division than to save on one character.
I still see no point in adding // sorry.
It would only add confusion for existing users, break some existing code further (possibly even hard to diagnose the issues that come with this change as most changes to GDScript will cause the parser to send errors rather then changing the output afaik), and it can be easily bypassed, as in it can be worked around with a handful of characters every time there is such a case.
break some existing code
There is nothing you can do about it, as it often happens with major updates. If this is the only argument against it, then it doesn't sound convincing. Backward compatibility is not only good but also evil.
read up, it isn't the only argument against it. it is one of the arguments but it isn't the only one and not the main one.
arguments are as follow:
/ operator is eliminated./ operator behaves differently._The discussion is looping, let's wait until more than 3 people take part in it._
I think the current GDScript division behavior is fine as it is. I wouldn't break compatibility for something that is not guaranteed to be a win in all situations.
Python deemed this important enough to break it going from Python 2 to 3.
Having lived through exactly this transition I have to say it was absolutely worth it. Our code is clearer now in expressing the underlying intention and we face considerably less division based bugs.
Since GDScript is heavily Python looking, it is confusing that it departs from it in that regard. I wasn't aware of it and assumed Python semantics so far.
Since GDScript is heavily Python looking, it is confusing that it departs from it in that regard. I wasn't aware of it and assumed Python semantics so far.
GDScript has nothing to do with Python besides being indentation-based as well. It's a misconception to think otherwise. Please, don't base your arguments on this, it's unrelated.
Coming from GameMaker it briefly confused me too. Someone told me "most languages assume integers unless told otherwise" which I thought was a fine explanation. The argument being used in this thread should simply be that of _standards_.
If we changed it, then instead of us it'd just be the other side getting the unexpected behaviour. Someone has to suffer.
Once again, I don't want GDScript to be more Python-like. The problem is that in most languages 5 / 2 == 2.5. Is there an important reason why we should go against the majority?
We can just copy this one piece from Python. In all other aspects, we can still think differently.
The problem is that in most languages 5 / 2 == 2.5. Is there an important reason why we should go against the majority?
Where do you get this idea? This is not true for the majority, just for a specific group of languages.
Where do you get this idea?
This is true for most scripting languages (JavaScript, PHP, Perl, Python, Lua, Dart, etc.). And GDScript is definitely a scripting language.
GDScript has nothing to do with Python besides being indentation-based as well.
pass, most keywordsand, or, in, % (formatting operator), etcprint(), str(), len(), range(), etcYes, GDScript is not Python. But these languages are very similar. So it's no surprise that people expect these languages to work the same way with simple things like arithmetic operators.
GDScript has nothing to do with Python besides being indentation-based as well. It's a misconception to think otherwise. Please, don't base your arguments on this, it's unrelated.
It does not matter what GDScript wants to be. Python is the highest ranked scripting language on the TIOBE index and currently on place 2 overall. This means that there is a majority of developers familiar with (and naturally assuming) Python semantics. So it is not just a matter of syntactic resemblance. Departing from the semantics of the most widely used scripting language offers a bug potential for all developers with such a background. It would be unwise to ignore this argument entirely.
Someone told me "most languages assume integers unless told otherwise"
Int/Int also naturally assumes the output is desired to be Int
Godot assumes same type as both of the operands (if both are the same type) are unless told otherwise, and that makes a ton of sense. It's as simple as that.
Thing is, that is a design decision, and having division force between real and int division instead of it deciding by the situation based on how it's programmed..
also here's an example of something that'd be broken with the force inbetween and would be way harder to work around:
4 vars: a,b,c,d
a is int 5
b is float 3
c is int 4
some function f1 does this and prints result
d=a/b
Output -> 1.66 (float)
somewhere later in the code in some function f2
b=c --> b is now int 4
call function f1
Output -> 1 (int)
This wouldn't be possible if / would force real division and // int division as the output would be float or int in both scenarios and this type of change where some variables aren't always the same type and expect to be treated differently is quite useful in quite a few scenarios.. True it's harder to keep track of constantly changing types, but working that around would make it much worse and would probably lead to plenty of Copied code left and right for every such case... meanwhile the workaround for your issues are simpler.. just turn one or both into a float if you want real division.
Also if you don't want to do float() you can always go var1 as float/var2 instead of float(var1)/var2 if you want more readable code that is..
Int/Int also naturally assumes the output is desired to be Int
naturally
This is not the case in mathematics:
3 - 5 = -2 -:(β,β) β β€
3 / 2 = 1.5 /:(β€,β€) β β
2 ** 0.5 = β2 **:(β,β) β β
The situation is a bit similar with +, which combines the operators of addition and string concatenation. However, unlike /, it at least does not allow mixing a number with a string, or a string with a number. If, according to your logic, <int> / <int> == <int> and <float> / <float> == <float>, what should be <int> / <float> and <float> / <int>? So this argument doesn't count.
And I still do not understand what inconvenience (except for breaking compatibility) creates the separation of / into / and //. The behavior becomes consistent and now only depends on _intent_ of programmer, and not on a subtle difference in the type of operands.
For example, Pascal is a statically typed language. However, it has <integer> / <integer> = <real>.
And <integer> div <integer> = <integer>. What is the problem here?
No need to blindly copy the behavior of C/C++, Java, etc., especially since they are not scripting languages.
Just read my example above. seperating would cause that use case to require quite a bit more code which isn't worth the small benefit of a seperation when that can be changed with a simple float() or with var as float.
on the int/float and float/int you misunderstood me, and i've clarified it up in an edit.. I ment if both operands were same type not different types.. in mixed types it depends and goes like vector>float, float>int and the like.
Nothing is clear from your example, sorry.
But I'm pretty sure the change never increases the number of lines of code. Only their length, sometimes. For one character. :smiley:
It does. quite a bit. it isn't as simple as you think. the issue is you can't change between real division and int division at runtime with / and // the way you can with the current system, meaning you'd have to add lines of code to check if it's real division or int division that you are expecting, and that is in a SIMPLE setup... a more complex setup would require many more lines of code to deal with it.. magnitudes more in fact..
you can't change between real division and int division at runtime
Explain what you want. This is a very strange formulation of the question. Give an example of a real-world problem that would require you to use the following function:
func old_div(a, b):
if a is int and b is int:
return a // b
else:
return a / b
It does not matter what GDScript wants to be. Python is the highest ranked scripting language on the TIOBE index and currently on place 2 overall. This means that there is a majority of developers familiar with (and naturally assuming) Python semantics. So it is not just a matter of syntactic resemblance. Departing from the semantics of the most widely used scripting language offers a bug potential for all developers with such a background. It would be unwise to ignore this argument entirely.
This is still a pointless comparison. Python is a general purpose language and pretty irrelevant to the gamedev domain. GDScript is purpose built language that is bound by that domain. Int math is more relevant in gamedev than in general programming. And it is faster than any proposed alternative when you actually rely on it.
And as I've mentioned before, people will still face that same behavior in our shader language. It doesn't matter where they will learn the importance of it, they inevitably will.
This is the case where familiarity is not that important. If anything, familiarity within the domain will be quite different.
you can't change between real division and int division at runtime
Explain what you want. This is a very strange formulation of the question. Give an example of a real-world problem that would require you to use the following function:
func old_div(a, b): if a is int and b is int: return a // b else: return a / b
This proves my point.. also you cherrypicked the quote, as the continuation explains and doesn't say this is impossible.
This is 5 lines of code, + turning every / into a function, adding another 4-5 chars min per / not to mention the fact that the proposal can be worked around with only as few as 7 total chars per needed case (2 if it's a hard coded constant like in your examples above where you turn a 2 into 2.0 in a line of script instead of it being a var that is 2)
I totally agree with https://github.com/godotengine/godot-proposals/issues/1866#issuecomment-731629540
There is no point in breaking compatibility for something that isn't a win for most situations.
It would be worth Breaking compatibility if it was something impossible/hard to work around even if it isn't a win in all situations (but only if it doesn't make the losing situations end up being as hard or harder to work around in the aftermath) however this change isn't difficult to workaround and just complicates stuff needlessly for fixing compatibility, adds confusion to the existing userbase and for what? to make it more in line with some existing scripting languages? When changing Languages naturally you should look up the basics of the new language.. this is what can be considered a basic of this language.
his is 5 lines of code, + turning every / into a function, adding another 4-5 chars min per / not to mention the fact that the proposal can be worked around with only as few as 7 total chars per needed case (2 if it's a hard coded constant like in your examples above where you turn a 2 into 2.0 in a line of script instead of it being a var that is 2)
Great calculations... You never gave an example of a case where you need this exotic behavior. In 99% of cases, either normal division or integer division is required, and not something incomprehensible, depending on the exact types of the operands.
No matter how you think about it, division and integer division are two different operations and should not be combined. Those who do not agree with this can combine the sewer with the water supply.
No matter how you think about it, division and integer division are two different operations and should not be combined. Those who do not agree with this can combine the sewer with the water supply.
Division and Integer Division aren't 2 different operations, and thus don't need to be seperated.
It is like saying that there should be 2 different operations to handle 5+5, one as strings and one as Ints/Floats when you can do "5"+"5" to get string and just have 5+5 return Ints/Floats
Note: using Ints/Floats as if a type of var is forced to float, 5 is still a valid float value and thus 5+5 can be Floats if the Vars are of the Float Type.
Your analogy is incorrect. int and String are completely different types, and int and float have one thing in common that they are both numeric. The following example describes this situation more closely:
#include <stdio.h>
int main() {
unsigned char a = 255;
unsigned char b = 255;
unsigned char c = a + b;
printf("%d\n", a + b); // 510
printf("%d\n", c); // 254
}
Following your logic, it would be <unsigned char> + <unsigned char> == <unsigned char>. But this is obviously wrong. If the result of addition does not fit into the range of operands, then this is not a reason to overflow the result. If the result of dividing an integer by an integer is not an integer, then this is also not a reason to just discard the fractional part.
The reason the division operator behaves this way in system languages like C is purely historical. Why new languages copy this limited behavior now? - is a mystery to me. I am also surprised by the retrograde thinking of some people. I'm tired of arguing, better argue with Niklaus Wirth.
var a: float = 5 / 2 # a == 2.5
var b: int = 5 / 2 # b == 2
var d = 5 / 2 # d == 2.5
var e = int(5 / 2) # e == 2
var f = 5 // 2 # f == 2
This is still a pointless comparison.
In my opinion, it is never pointless to look at what skilled language designers have come up with. The Python community has put a lot of thought into this. It was a difficult decision to go for the breaking change, but there is simply a huge bug potential if / can return int if the operands happen to be all integers. Imagine doing a division somewhere without knowing what the user has passed in. This is such an infamous bug:
def f(a, b):
# developer locally assumes a and b are already casted to float
some_division = a / b # bug
f(5.0, 2.0) # works
f(5, 2) # triggers bug
This is one of these ugly bugs: The function may work for a long time if it is called with floats. As soon as integers creep into its input it misbehaves silently. And this bug can occur in any context, be it general purpose programming or game dev.
See PEP 238 for a more detailed design document behind this decision. In my opinion, it certainly wouldn't hurt to cross check the reasoning behind it instead of narrow-mindedly discarding it as "pointless, because GDScript for game dev".
Python is a general purpose language and pretty irrelevant to the gamedev domain. GDScript is purpose built language that is bound by that domain.
No one disagrees on that, but that does neither justify why the / operator should have an error prone behavior, nor does it respect the fact that there is a certain probability of Godot developers having a Python background (for instance, all in our development team have).
Int math is more relevant in gamedev than in general programming.
That is questionable, but wouldn't that even count as an argument why a dedicated integer division operator would be beneficial in game dev?
And it is faster than any proposed alternative when you actually rely on it.
Why do you think so? In practice there should be no difference in performance at all. If anything explicitly distinguishing between "float division" and "integer division" gives the interpreter a way to optimize, because it can know beforehand what kind of conversions are necessary. In Python the introduction of the explicit operators has actually sped up divisions:


I would be surprised if in GDScript there isn't a potential for performance optimization as well, because:
@bluenote10 are you sure that the performance gain you see is because of that specific change and not general performance improvements from so many versions there were in between? I mean with such a Huge gap in versions.. There could be plenty of optimizations that could have taken place skewing the result. To get an accurate feel of things there has to be the fewest number of variables in a test, and so many versions in between leaves room for a TON of variables that can affect the test. Optimal test would be to have a same version with only that one change for an accurate before & after and until then the result is more or less invalid as it isn't possible to determine beyond the reasonable doubt that the performance gain is actually from that 1 change and not the endless changes, optimizations and fixes in between the 2 tested versions.
@dalexeev maybe other languages copy that behavior because it makes more sense to their creators to stick to ints if both operands are ints instead of switching from a pair of ints to a float.. Also I come from a mostly Python Background to Godot, never had an issue with GDScript in that manner.. I just read up on the GDScript Basics, saw it isn't a thing and honestly I'm glad as I found that very annoying in Python... Having 2 operations for essentially a single thing is just silly especially when it can be all handled in one.
Also, one more thing, Just because someone disagrees with you doesn't mean that they're wrong. Personally I'd be like OK if it was handled the Python way (I'd still find it annoying) but just implementing it and breaking compatibility when there's a super easy workaround without a good reason is a big no-no and so far the only claims that do make a shred of sense that are for implementing are:
While the issues from implementing such a thing are:
dalexeev presents
Special Table Explaining What Is Good And What Is Bad
============== CURRENT ============== ============== PROPOSAL =============
# Integer division # Integer division
func f1(a: int, b: int) -> int: func f1(a: int, b: int) -> int:
return a / b return a // b
# Integer division # Integer division
func f2(a: float, b: float) -> int: func f2(a: float, b: float) -> int:
return int(a / b) return a // b
# Real division # Real division
func f3(a: int, b: int) -> float: func f3(a: int, b: int) -> float:
return float(a) / b return a / b
# Real division # Real division
func f4(a: float, b: float) -> float: func f4(a: float, b: float) -> float:
return a / b return a / b
# Nobody knows what the division
# will be here. And this is BAD.
func g0(a, b):
return a / b
# Integer division # Integer division
func g1(a, b): func g1(a, b):
return int(a / b) return a // b
# Real division # Real division
func g2(a, b): func g2(a, b):
return float(a) / b return a / b
@bluenote10 are you sure that the performance gain you see is because of that specific change and not general performance improvements from so many versions there were in between?
No, I'm not sure, but digging into the Python bytecode isn't the point here. Please check my last two bullet points again, why it can only be beneficial in terms of performance from a theoretical perspective as far as I can see.
Having 2 operations for essentially a single thing is just silly especially when it can be all handled in one.
Please read PEP 238 and ask yourself why Python has made this breaking change. I have never seen a Python developer complaining about this improvement, especially not anyone who has started back in the Python 2 days with the frequent bugs caused by the old division behavior. I have followed the migration of several big projects from Python 2 to 3, and in all of them it was clear that this change has reduced bugs, with all developers involved seeing it as a step forward.
... without a good reason ... claims that do make a shred of sense ...
To be clear again, the major points purely from a language perspective are not performance but (ordered by priority):
a / b it is clear that this will return a float; when seeing a // b it is clear the result is an integer; whereas nothing can be said about a / b with the current division.I fully agree with the basic idea of this proposal, i.e. making sure that a / b will always be close to the mathematical division result (as close as possible with floating point arithmetic) - with the main reason being to avoid bugs caused by reasonable assumptions people make in a dynamically typed language, see PEP238 as @bluenote10 already mentioned.
But I think there are a few details that are unclear:
The behavior of integer division // is tied to the modulo operator %, see Wikipedia-Modulo for the full details. And there's a choice in what to do when faced with negative numbers. Python has the arguably better behavior of floor-division (round to negative infinity), see Guido's reasons for that, as do many other high-level languages - while Fortran, C and friends do truncated-division (round to zero), probably for historical hardware-support reasons. Since GDScript is mostly a thin layer on top of C++ it's probably best to keep that behavior and make // truncated-division, but it would be an opportunity to change that, if desired? (That would also mean changing the sign of the result of a % b in some cases.)
The return type of a / b should obviously always be float even if the result would fit in an int, but it's not clear to me that the return type of a // b should always be int. (For reference, in python, arguments are always first converted to a common, "larger", type, so int // int returns an int, but float // float or even float // int returns a float. Also: float % float is defined, and consistent with floored division, i.e. different from fmod which is also available as math.fmod in python.) I think that the main arguments for having float // float return a float are to make it consistent with type coercion rules for other mathematical operators and to avoid surprising type changes when it is used in larger expressions. In keeping with GDScript's thin-layer-over-C++ nature, an implementation would probably delegate type coercion to C++ and then the result would probably be a float.
I agree with the 2nd point about not changing types should this get implemented. (as in float//float=float and not float//float=int)
As far as performance goes, I find it possible that Explicit casts cause a slowdown, an easy way to see if that's where the performance gain is would be to exactly copy the Python 2 code to Python 3 code (as in force a to be float) and see what Python 3 performance would be (as in how much it suffers from the cast)
@strank
1. I think we need to keep the current behavior used in many languages (truncated-division). See example:
x | A B C D | E F
=====|=============|=======
> -6 | -2 -2 -2 -2 | 0 0
-5 | -1 -2 -1 -2 | -2 1
-4 | -1 -2 -1 -1 | -1 2
> -3 | -1 -1 -1 -1 | 0 0
-2 | 0 -1 0 -1 | -2 1
-1 | 0 -1 0 0 | -1 2
> 0 | 0 0 0 0 | 0 0
1 | 0 0 1 0 | 1 1
2 | 0 0 1 1 | 2 2
> 3 | 1 1 1 1 | 0 0
4 | 1 1 2 1 | 1 1
5 | 1 1 2 2 | 2 2
> 6 | 2 2 2 2 | 0 0
A == int(x / 3) == x // 3
B == floor(x / 3)
C == ceil(x / 3)
D == round(x / 3)
E == x % 3
F == posmod(x, 3)
(That is, those who need to simulate Python behavior for negative numbers can use floor(x / a) and posmod(x, a) instead of x // a and x % a, respectively.)
2.
The return type of
a / bshould obviously always befloateven if the result would fit in anint
Yes, 8 / 2 == 4.0.
but it's not clear to me that the return type of
a // bshould always beint
I think differently. It's strange that Python made such a decision. For example:
$ python3 -q
>>> 2.73 / 0.7
3.9000000000000004
>>> 2.73 // 0.7
3.0
If the result of a // b always has a fractional part of 0, then what's the point of using float instead of int? (This is especially strange in Python, because it has support for BigInteger out of the box, i.e. there shouldn't be any problems with some large float values that cannot be represented in int).
After all, it makes sense that real division returns float values, and integer division returns int values.
Float//Float=Float no discussion there, force changing it to int is pointless as this could be used in scenarios where you expect a float and there is no reason to think that F//F=I which would cause issues akin to what you are using as an argument for the division separation that you yourself pointed out. F//I and I//F should possibly be Floats but I do see them returning int.
Also as a side note, you can run Python if you so wish in Godot, there are addons/modules that support it.
as this could be used in scenarios where you expect a float
Where a float is expected, int values are also accepted without problems. But where int is expected and float is received, the Narrowing Conversion warning appears:

This is the basic thing that most people should understand. I wonder why I have to explain it.
In addition, as another argument in favor of this proposal, I remembered that there is Integer Division warning:

This is what the debugger looks like if I turn on these warnings (they are enabled by default, I once disabled them because they bother me more than they help me, like the Return Value Discarded warning):

I clicked on one of the Integer Division warnings. Here's what Godot warns me about:
var row_count: int
# ...
row_count = file.get_len() / 2 # <-- Integer Division warning
Also as a side note, you can run Python if you so wish in Godot, there are addons/modules that support it.
If you recommend this to me, then I do not need your recommendations.
@mrjustaguy I don't want to get personal, but it looks like you have certain problems understanding things. Please be at least not so categorical:
Float//Float=Float no discussion there
You misunderstood me clearly, often times when you're dealing with identical types, you want the outcome to be the same type as the operands (emphasis on both operands being the same type) unless you say otherwise as that is the expected behavior for other operations (a behavior that the separation would also break as int/int=float however that at least has the argument that it's more mathematically accurate if you've got decimals in the division that is, else floating point error makes it just a tiiiny bit less mathematically accurate). When dealing with different type operands then there's possibly a discussion to be had. (Again, if the separation were to be done)
I want to point out that without a consensus there won't be any change. Without a clear interest from the community I prefer to keep things the way they are now.
Maybe you people want to take a break from this discussion and come back in a few days with a clear head, as this do not seem to be moving forward.
when you're dealing with identical types, you want the outcome to be the same type as the operands
It depends on whether it makes sense or not. Remember the mathematical definition of a function.

Sometimes a set (in this case, a pair of identical sets) is mapped into itself: f:(A,A) β A. In this case, you are right.
But it so happens that the set is mapped into its own superset or subset. Or even another set.

Therefore, the type of the result is not always the same as the types of the operands. I have already given an example above that <unsigned char> + <unsigned char> != <unsigned char>.
God please no.
if you want to do a float division just make sure nothing is a int.
// is ugly and confusing
I want to point out that without a consensus there won't be any change. Without a clear interest from the community I prefer to keep things the way they are now.
Understandable, but the problem is that casual followers can take a somewhat shallow look on such topics. Anyone who is willing to dive into the motivation given e.g. by Guido van Rossum (or study the design of other dynamically typed languages that have e.g. opted for / and div for the same reason) should realize that the reasoning is sound and there is general consensus that one division operator works well in statically typed languages, but dynamically typed languages are generally better off separating them into two to rule out a common source of bugs. Of course, not everyone has the time to do that. A democratic vote can lead to an inferior overall solution if the vote gets biased by superficial "please no" reactions (please don't take this as an offense @Shadowblitz16, most likely you simply didn't waste too much time on the topic, in which case this is a totally sensible and understandable reaction :heart: ).
@mrjustaguy
- Easy to workaround
I just wanted to point out that the work-around actually isn't that straightforward. Casting to float is technically not entirely correct. Imagine a function normalize_values(values, reference_value) that takes an array of values and normalizes all values with respect to reference_value, i.e., it returns a new array with all values transformed as value / reference_value:
func normalize_values(values, reference_value):
var new_values = []
for value in values:
new_values.append(value / reference_value)
return new_values
The interesting thing is that such a function can be "generic" (= uses duck typing), i.e., it can operate on anything like:
values_normalized = normalize_values([5, 3, 4], 3) # (1)
# should be [1.666667, 1, 1.333333]
values_normalized = normalize_values([2.1, 6.2, 4.2], 2.1) # (2)
# should be [1, 2.952381, 2]
values_normalized = normalize_values([Vector2(1, 1), Vector2(2, 3)], Vector2(2, 2)) # (3)
# should be [(0.5, 0.5), (1, 1.5)]
values_normalized = normalize_values([Vector3(1, 1, 1), Vector3(2, 3, 4)], Vector3(2, 2, 2)) # (4)
# should be [(0.5, 0.5, 0.5), (1, 1.5, 2)]
In the current form, the function contains the infamous division bug value / reference_value which gives the wrong result in case (1).
Using the often mentioned work-around float(value) / reference_value makes (1) and (2) work, but actually prevents proper duck-typing, i.e., it would break (3) and (4), because float(Vector2(1, 1)) doesn't work.
A technically correct work-around would be to use the expression value * 1.0 / reference_value because it is the only operation that is fully duck-typing friendly.
Such duck typing use cases are arguably rare, but just pointing out that the work-around has some subtleties -- and in fact the "best" work-around hasn't even been mentioned.
While I still think that this proposal would be the best option I also have an idea that would be fully backwards compatible while providing a means of preventing the most common forms of the division bug. I may write an alternative proposal if I find the time.
Ok I know what I'm about to say is probably dumb.. But how about this style of compromise? (if doable ofc)
Keep / as is, and add i/ as python style // and f/ as python style / that way everyone wins, there is no compatibility breakage, integer and real division python style exist in a simple manner, and all can be clarified in the docs with few sentences.
Keep / as is, and add i/ as python style // and f/ as python style / that way everyone wins
That is a grammar nightmare :)
if you want to do a float division just make sure nothing is a int
Easy to workaround
You can also advise simply not to use division, but replace it with multiplication:
a = b * 0.142857 # Instead of `a = b / 7`
Although, wait a minute:
a = b * 1.0 / Ρ # Can be read as `a = b * (1.0 / Ρ)`
//is ugly and confusing
That is, a certain beauty is important to you, and not logical and consistent behavior? Let me guess what the problem is. // looks like the beginning of a comment in C-like languages and it ruins your peace of mind?
I think people are just afraid of changing what they are used to. They do not want to see the problem and simply deny its existence. Surprisingly, without even knowing that there was such a problem in Python, without reading PEP 238, I came to similar conclusions. The fact that Guido van Rossum is on my side gives me confidence. It is very easy to dislike, while understanding the problem takes time and desire.
Also, I would like to quote PEP 238.
We propose to fix this by introducing different operators for different operations
The classic division operator makes it hard to write numerical expressions that are supposed to give correct results from arbitrary numerical inputs. For all other operators, one can write down a formula such as
x*y**2 + z, and the calculated result will be close to the mathematical result
The problem is unique to dynamically typed languages
It is the opinion of the authors that this is a real design bug in Python, and that it should be fixed sooner rather than later. Assuming Python usage will continue to grow, the cost of leaving this bug in the language will eventually outweigh the cost of fixing old code -- there is an upper bound to the amount of code to be fixed, but the amount of code that might be affected by the bug in the future is unbounded.
My take on this is that int / int = int makes more sense on typed or hybrid languages, while int / int = float makes more sense on purely dynamic languages.
The reasoning behind this is that if you do:
int a = 5;
int b = 2;
int c = a/b;
There will be several implicit conversions that you are not aware of. Sure, you can start checking everything and throw warnings and errors, but the intent here is clear so the implicit conversions are unnecesary.
While its true that GDScript intends to be Python-like, it's also true that a large amount of users use GDScript with type hints, for which following the Python3 rules makes less sense. If GDScript was purely dynamic, I would feel more inclined to follow Python3 on this one, but it's not the case.
Additionally, another problem you have is that many Python users don't even know the // operator exists and honestly no one reads the language documentation that much in depth, so for integer division by default you will always get the slower/less efficient path if you don't know the alternative. The same would happen with GDScript.
I also use typed GDScript, but sometimes I still forget about this behavior (for example, in expression ceil(INT_EXPR / 2), I may not see the problem, especially if INT_EXPR is a composite expression). My first programming language I studied in school was Pascal, which is statically typed, however it has two different operators: / and div.
There are many abbreviations in C/C++ that confuse newbies. While the GDScript style guide recommends using and and or instead of && and ||, for example. It seems to me that one of the values ββof GDScript, like Python and Pascal, is simplicity. And this proposal makes the code easier to understand.
Also, type hints are optional (and, as far as I know, won't be required). In addition, even in typed GDScript elements of arrays and dictionaries, signal arguments remain untyped.
While its true that GDScript intends to be Python-like, it's also true that a large amount of users use GDScript with type hints
True and more are turning that way, especially with the Performance gains from using them that are being worked on, as noted in https://godotengine.org/article/gdscript-progress-report-typed-instructions
How big is the performance loss when using int(x / y) instead of x // y, and is it so important in scripting languages? It seems to me that inefficient newbie code itself (for example, incorrectly written loops) will introduce much more performance losses than using a less efficient operator.
(What strained arguments people give, just not to admit to themselves that the main argument is their habit.)
I come from C++, but I still made mistakes with current GDScript division several times.
How about 5 / 2 is 2.5, and div(5, 2) is 2?
@starry-abyss This is a secondary issue. x // y, x div y, div(x, y) or x \ y.
But // is better, because GDScript is Python-like.
In fact, there are two proposals in this issue:
/ operator consistent (this issue caused the most controversy).// operator (x // y is just int(x / y)@reduz I would argue that the person who writes:
var a: int = 5
var b: int = 2
var c: int = a / b
and expects truncated division, is also the person that reads language references and would know to use // if they really care about the efficiency gain. However, the person that writes:
var a: int = 5
var b: int = 2
var c = a / b
will probably be bitten by c not being 2.5. That's the person this will help the most.
So rephrasing your take, I would say: int / int = int makes some sense for purely strongly typed languages, while int / int = float makes more sense on hybrid or dynamic languages. Of course your take has much more weight here, but maybe that reasoning makes sense?
Yeah the 2nd case person will probably get bitten by c not being 2.5, however it is reasonable to assume that the person has some understanding of programming and will do such a division in the early days of their GDScripting while still trying GDScript out to see if they like it and notice the output is 2 and not 2.5, and then be like "Oh yea, a and b are both ints, so I guess c is also int, let's see what happens when one of them is a float" and learn that way.
True, not everyone will do that, but in my experience it's quite a common thing to start bashing such simple things seeing the outputs of said things and learning that way if they're not the ones to read the language references, so such people learn with trial and error and maybe a tutorial left and right when they hit a snag..
The type of person that'd be really bitten by c not being 2.5 is a total noob programmer with little if any previous programming experience (especially if it's also a person with such overconfidence in their abilities that they need no tutorials or anything, however that is thankfully a very rare case) and that person's code would look more like:
var a = 5
var b = 2
var c = a / b
In this scenario not getting 2.5 would be a true "What the hell is going on!?" moment and would probably end up with a misuse issue here on github with a bit of a rant... On the other hand, such a 1st time programmer would actually be drawn to Visual Scripting more so...
Well, given there is no agreement about this, even within core developers, and given the user community is also pretty torn on it (at least based on a crappy twitter poll with 1300 votes), I guess the best will be to take no action for the time being.
Additionally, another problem you have is that many Python users don't even know the // operator exists
That is relatively unlikely. For years almost every Python 2 source file has started with the infamous from __future__ import division line -- which was one of the first things ported back from Python 3 to Python 2. This has put so much emphasis on the division thing that it is hard to overlook, and was often one of the first things for a junior developer to read up on.
Well, given there is no agreement about this, even within core developers, and given the user community is also pretty torn on it (at least based on a crappy twitter poll with 1300 votes), I guess the best will be to take no action for the time being.
Taking no action on divisive issue is an action in itself. Agreeing to disagree that benefits one side of argument over other is not good enough on issue like this in my opinion.
If you believe that current solution is good one. I can disagree but respect that. If you are keeping current solution just because a lot of people are used to it that way and issue is causing division that sounds like a bad reason to keep something. 4.0 is the opportunity to rewrite things keeping things the same "because they always have been" is bad take IMO. Some things should be kept because they are good but if primary argument for an issue is "we are used to it that way" it's bad argument to make. Because every new person who will ever use engine in a future is not used it that way and will need to understand quarks like this and try to understand why Mathematical operation my 6 year old son can do is something Godot gets wrong.
If you are keeping current solution just because a lot of people are used to it
Isn't this one of the main reasons put forward in support of changing it? That people want it that way because other languages do it that way?
I feel like people are way too emotionally invested in this change for reasons I can't figure otu. If the change doesn't happen, all we have to do is be explicit when using division. Not hard. If the change does happen it sounds like it opens a bunch of doors that need to be evaluated and then closed.
I don't want to have to use two different operators for the same thing.
It also add another operator needed to be overloaded if overloadable operators are ever added
Also if gdscript ever support alternant c style syntax for c developers then there is a ambiguity issue that can't be resolved
This is honestly the reason why static typing should be inforced.
If you get float division because your dividing two untyped numbers that actually what is suppose to happen. numbers in the real world are not integers.
if you need a integer division from two floats floor it after dividing
Taking no action on divisive issue is an action in itself. Agreeing to disagree that benefits one side of argument over other is not good enough on issue like this in my opinion.
I am not saying the opposite. Taking no action means that, as this is so divisive, the approach that users have been using up to now has more merit than the new one (as everyone is used to it, be it the preferred one or not), so the proposal won't go through for now, as it's not a clear improvement for most users.
This is similar to when we discussed removing && and || from GDScript and only leaving "and" and "or" for consistency, there was very strong opposition from users so no action was taken. Other changes, like adding annotations, or replacing yield with await, were a lot more welcome by users given they were a clear improvement.
@dalexeev since my github issue was the one that you referred to, I just wanted to point out that I am not exactly a newbie, so even more experienced devs can get caught out by this :)
It was mainly the change to Vector2i use for the viewport.size, unknown to me, that tripped me up, or I would have realised my mistake more quickly. Over the last month I have mainly been using Python and I often has that brainfart moment when the 2 get mixed up! Additionally there are some weird typing bugs in master at the moment, that I had been running into when porting my game from 3.2 to 4. So I just assumed at the time there may have been a bug.
Anyway, I agree with your proposal, but I think there will be too much resistance in the community for this to get much traction.
You say your main argument is that the current behavior is bug-prone, but for most game devs who use other engines doing it the python way is much more bug-prone. Most game engines use C# or C++ for writing game logic, both languages use C-style divisions.
It really comes down to who the main target audience are for Godot. Users who are completely new to (game) development will probably have an easier time with python style divisions, but most experienced game developers who try out godot will run into issues. I'm not sure whether a poll like the one on twitter or votes on the initial issue here is the best way to decide how to do this; from a UX standpoint it makes much more sense to evaluate the main target audience of the engine (instead of the loudest voices) and do whatever makes most sense to them.
Most game engines use C# or C++ for writing game logic, both languages use C-style divisions.
C-style division makes sense (or can be reasoned about at least) for those and other statically typed languages because you know at compile-time which type of division will happen. I think the bug-prone part people are talking about here is that in GDScript, the same code could behave differently depending on the runtime type of the arguments, so your code could work as expected almost all the time but occasionally break in hard-to-debug ways if you accidentally used the wrong types somewhere.
GDScript is not Python, but even more GDScript is not C. There are a lot of C/C++ developers here on GitHub, but keep in mind that many Godot users are not on GitHub. And these users usually have less programming experience, or they have programmed in friendlier languages like JavaScript, Lua, GML, etc. The scripting language must remain scripting. Scripting languages have their own traditions, one of which is the close-to-mathematical behavior of the division operator.
Do you think all GDScript users use type hints, enums, bitwise operators, getters, setters, etc.? I am not saying that this should be removed from the language. But the division operator is used by everyone, not just advanced developers. Let's not be snobbish and selfish, for whom the habit and economy of one character means much more than fixing a bug in the language design. High-level languages should hide the low-level implementation details.
I don't want to have to use two different operators for the same thing.
No, these are different operations, no matter how much you want to argue otherwise. Both Guido van Rossum and Niklaus Wirth confirm that these are two different operations. The fact that C/C++ uses the same operator for these two different, albeit similar, operations does not mean anything. It is obvious to everyone that there is no need to copy pointers from C++ to GDScript. Therefore, the division operator in GDScript does not have to be the same as in C++.
I don't want
As I said: this is the main argument of the opponents. :smiley: It doesn't matter which person likes more: C++ or Python. The context is important. When a person writes in a scripting language, he must respect the style adopted in scripting languages, no matter how super-duper cool C++ developer he is.
Above I was "objected" that Godot has the ability to use Python if I want Python. So I can also "argue" to you that Godot has the ability to use C# and C++ and C, if you like it better. But we should not be guided by emotions, but should answer, what is more like GDScript: Python or C++? Should GDScript be newbie friendly or have a bunch of shortcuts that only seasoned developers can understand?
This is honestly the reason why static typing should be inforced.
GDScript type hints are still optional in Godot 4.0-dev. This cannot be ignored.
Another quote from PEP 238:
In a unified model, the integer 1 should be indistinguishable from the floating point number 1.0 (except for its inexactness), and both should behave the same in all numeric contexts. Clearly, in a unified numeric model, if
a==bandc==d,a/cshould equalb/d(taking some liberties due to rounding for inexact numbers), and since everybody agrees that1.0/2.0equals 0.5,1/2should also equal 0.5.
I am not saying the opposite. Taking no action means that, as this is so divisive, the approach that users have been using up to now has more merit than the new one (as everyone is used to it, be it the preferred one or not), so the proposal won't go through for now, as it's not a clear improvement for most users
Issue I have with divisive proposals are that most users are not checking this repository daily. You simply cannot make this kind of decision after 3 or 4 days of issue being active. Share this in communities where users not contributors are. Post it on reddit, make a blog post on godot site etc. To actually gain what users NOT contributors want. People active in this repository are not representative of average user.
Devisive decisions like this should be discussed with a wider subset of community. Less than 20 people had discussion here while there are several 10s of 1000s of godot user. Views here represent less than 0.01% of Godot community
Users who are completely new to (game) development will probably have an easier time with python style divisions, but most experienced game developers who try out godot will run into issues.
More experienced developers will easily understand why 5/2 == 2.5. The error of getting a float where an int is expected is much easier to catch than the reverse error.
var a = 5 / 2
print(a) # 2
# A newbie expected 2.5, but received 2, without warnings
# (for some reason, the Integer Division warning did not work in this case).
# I simulate 5 / 2 with Godot 3.2, get 2.5
var b: int = 5.0 / 2 # <-- Narrowing Conversion warning
print(b) # 2
# Experienced developer: 1. got a warning 2. it even worked right!
Can't you see some injustice towards the newbie?
Moreover, functions that expect an int continue to work correctly when receiving a float, just a warning appears. If we do not replace trunc division with floor division, then most of the code will continue to work without errors even without change, even for negative numbers, despite the change in the behavior of the division operator. And in those rare exceptions where it is still important, the error is much easier to notice, because a fractional number will come out where an integer was expected.
# Node.get_child(idx: int) -> Node
print(get_child(0.5).get_index()) # <-- Narrowing Conversion warning
# prints 0
It absolutely validates the laws of logic
if 2 == 2.0:
/this is true
if 5 == 5.0:
/this is true
if 5/2 == 5.0/2.0:
/ This is false
what is worse it's inconsistent
if 6/2 == 6.0/2.0:
/this is also true
You have 2 values that are equal going through the same mathematical process and providing different results.
Either 5/2 == 5.0/2.0 must be true or 2 == 2.0 should return false. so there is logical consistency. If int is not equal to the respective floating point new user may understand that but if int is sometimes equal to float and sometimes not this is confusing.
I agree with https://github.com/godotengine/godot-proposals/issues/1866#issuecomment-733543993
This should be something for the wider community to get into, possibly in some poll like https://godotengine.org/article/godot-community-poll-2020 however what the poll would actually look like is.. a great question.. Could be Questions like:
What do you expect from:
1)
int/int=?
A) int
B) float
2)
5/2=?
A) 2
B) 2.5
Background (multiple choice Q):
1)
Previous Languages
A) Python
B) C++
C) Lua
D) JavaScript
....
X) None - new to programming
X+1) Other - Name some
2)
Previous Engines/Libs
A) Unreal Engine
B) Unity
C) LibGDX
...
X) None
X+1) Other - Name some
Preferences:
1)
Static VS Dynamic typing
A) Prefer Static
B) Prefer Dynamic
C) Undecided
2)
Division Operators
A) Just / with the current behavior (5/2=2, 5.0/2 or 5/2.0 or 5.0/2.0 or float(5)/2 or 5/2 as float or some such =2.5)
B) Split current mixed / into real division (/ always returns floats - 5/2=2.5) and integer division (// always returns integers - 5//2=2)
C) Don't Care
D) Other - If there are other ideas
3)
Would Change (splitting / into / and //) affect you in a negative or positive manner?
A) Very Positive
B) Positive
C) Both ways
D) No effect on me
E) Negative
D) Very Negative
Again, this is just an idea of what such a poll might look like while providing both the desires of the community and some reasoning behind their decisions.
@reduz, you are definitely a very talented C++ developer and compared to you I have no weight in the community at all.
But tell me, would you like to have <unsigned char> + <unsigned char> == <unsigned char> in C++? And to get around this, would you have to do (int) <unsigned char> + <unsigned char> == <int> every time? (See comment for an example.)
Maybe we should admit that the division operator in C++ behaves this way not because it is the best wayβ’, but for historical reasons. And for example in Pascal <integer> / <integer> = <real> because it is more correct in terms of mathematics.
Itβs bad to be a fanatic about anything. It's bad to be a Python fanatic, it's bad to be a C++ fanatic.
Forgive me for the lecture. I want to say that your personality inspires me. You do what you are interested in, what you created yourself, while a huge number of people around the world are forced to work without interest for the sake of money. But despite my respect for you, I believe that on this issue you are absolutely wrong. And as benevolent dictator for life, you should be more forward-thinking than many ordinary Godot users.
I often utilize flooring of int division. In the case of this proposal, it would mean that in the background engine will cast elements (or even all of them) as floats, will create some floating-point precisions (in this situation probably doesn't matter) and in my use cases, I will need to cast the result back to int. That means more processing goes to the thing that shouldn't be there in the first place, meanwhile so many are whining about GDscript performance.
If I would want to get float division I will cast exactly the one element that's needed for float division.
it would mean that in the background engine will cast elements (or even all of them) as floats, will create some floating-point precisions and in my use cases, I will need to cast the result back to int
No, if you do <int> // <int> == <int>, then no conversions to floats will occur, everything will be exactly the same as before, perhaps even faster (see comment). This is one of the reasons why I crossed out the sentence "actually x // y is just int(x / y)" in my comment above.
Performance is good, but you don't have to be a performance fanatic. According to the information that I managed to find, integer division takes 12-44 processor clock cycles, floating-point division 37-39 clock cycles, a C/C ++ function call takes 25-250 clock cycles, a call to the OS kernel requires 1000-1500 clock cycles, a C++ exception spends about 5000 clock cycles, one thread context switch takes about 10,000 clock cycles.
This should be something for the wider community to get into, possibly in some poll
The last thing that any lasting decision needs is involving the whole community to make it. Design by committee is an approach that results in things that are not thought through and are based on favoritism, vocal majority's opinion or inter-personal relationships.
What we need for GDScript is a small group of individuals. We need authority. People that are responsible and interested in making a language that follows some predefined path instead of making abrupt changes based on community votes on social media. We need a strong idea behind GDScript, a plan and a goal for the language. The language needs to know what it wants to be. And we need people to decide on that without any reliance on public opinion and instead base it on what is best for the engine instead. Only then changes like this one can be discussed with some ground below them, going beyond "Hey, I want GDScript to be Python" and "Hey, this is not Python, go away".
Many of us have seen this same level of passionate yet empty flaming happen with vnen's GDScript 2.0 PR. It was a disaster of opinions. And programmers tend to get very much opinionated on things, ofttimes toxically so. We need authoritative people who would immediately stop ideas that are wrong and harmful for the particular path that GDScript takes before discussions turn into farce. That's what we need. Not a poll.
The last thing that any lasting decision needs is involving the whole community to make it.
I fully agree with @pycbouh (with the minor clarification, that obviously discussing should always be allowed for anyone). Fun fact from PEP 238, which by the way is now almost 20 years old:
Because of severe backwards compatibility issues, not to mention a major flamewar on c.l.py,
And here we are, 20 years later, involved in the same flamewar ;).
This decision is not just a matter of taste. If you open a poll, how much time do people spend on average to make a conclusion, seconds, minutes? The implication of such a decision are deep and subtle, it is hard to see which bugs are there, which can be prevented, what is the impact on duck typing, how does it relate to other language feature, future directions of a language, does it have performance effects etc.
I'm pretty sure Guide van Rossum would not have been able to fix this design bug in Python 2 if he had opened a poll back then. At that time, developers with a C/C++ background were ubiquitous and most likely the participants in the poll would have briefly voted according to their habits without much reflection. The idea certainly was quite innovative at that time, and support by a majority unlikely. Now 20 years later, Python developers are (typically) happy to have one source of problems ruled out.
If there is one thing to learn from Python: Sometimes a benevolent dictator for life is needed.
We need authoritative people who would immediately stop ideas that are wrong and harmful for the particular path that GDScript takes before discussions turn into farce. That's what we need. Not a poll.
Sadly, wrong and harmful are not objective terms here, and a handful of people doing things for a community without listening to feedback would end up in it going in an undesirable path for said community... I mean what if await was unpopular with the group of individuals running things for whatever reason, while the vast majority of the community wants it over yield?
One cannot know if they are doing the community a service without first understanding the community they are serving and for that you need data, and polls are the best way of getting data for these types of scenarios, especially when you get other data about the community that can also point you to the roots of their decisions/desires allowing you to even better understand the community and it's needs.
To clarify, flamewars, while highly unproductive, are OK (as long as they respect the Code of Conduct of course).
We don't take decisions based solely on the discourse on a given proposal, nor on the results of Twitter polls, or any other kind of "global" community consultation.
The decision making is in the hands of the area owner (here @vnen, to some extent @reduz) and core contributors at large. Discussions like this one can help provide arguments for or against a given technical change, but whether it's done eventually is evaluated by core devs on the merits of the proposal for their vision of how the engine (here GDScript) should behave.
The discussion so far (here, on Twitter, on IRC) shows that there is no consensus in the community. There's also no consensus among core devs in favor of the change, so this won't change for now. Opinions can still change, but don't ask us to reach a decision now when it's a 50/50 situation (both in terms of acceptance, and in terms of technical drawbacks to each outcome).
@pycbouh Making a decision requires responsibility. @reduz doesn't want this, he put it in the hands of the community (or rather, his twitter) and said that it would be as people said. @akien-mga objected to him that the wording "int / int == int vs int / int == float" could be misleading for those who are not familiar with the topic. Which is really so. If we decide by vote, then the poll suggested by @mrjustaguy is better than the Twitter poll.
To be honest, I did not expect such a big reaction from people, it was a little stress for me. I can imagine what responsibility is now on @reduz. The last 4 days in the Godot plan, I have paid attention only to this proposal. I believe that all the arguments in this topic have already been presented, so I will no longer write my comments (unless I am asked a question personally).
Me and @bluenote10 did everything we could. We, in my opinion, have given many brilliant arguments, dug up PEP 238, which is soon 20 years old. Now everything depends only on core developers, on their desire to read, consider and weigh all the pros and cons.
Finally, I want to thank @mrjustaguy for his questions. They were very annoying and strange in places, but thanks to them, this topic now has many arguments from our side. Oh yeah, and forgive me for my terrible English. :smiley:
Sadly, wrong and harmful are not objective terms here, and a handful of people doing things for a community without listening to feedback would end up in it going in an undesirable path for said community... I mean what if await was unpopular with the group of individuals running things for whatever reason, while the vast majority of the community wants it over yield?
One cannot know if they are doing the community a service without first understanding the community they are serving and for that you need data, and polls are the best way of getting data for these types of scenarios, especially when you get other data about the community that can also point you to the roots of their decisions/desires allowing you to even better understand the community and it's needs.
@mrjustaguy You provide a valid point about needing the data, but at the same time there is a fallacy. You don't need to have an "objective" opinion that satisfies everyone. You need an expert opinion, an authority that decides what's the best course of action is. You need to understand your domain, both the technical part and the people, but you still need a person making the final decision, sometimes even if they go against an overwhelming amount of people supporting some change or acting against such change. People in large quantities often don't know what they really want and cannot think about such things critically and rationally.
As such, even if you gather data, asking direct questions is often harmful for the end result. That's a whole other science.
The discussion so far (here, on Twitter, on IRC) shows that there is no consensus in the community. There's also no consensus among core devs in favor of the change, so this won't change for now. Opinions can still change, but don't ask us to reach a decision now when it's a 50/50 situation (both in terms of acceptance, and in terms of technical drawbacks to each outcome).
@akien-mga I don't think anyone is asking for that just for bringing issue to wider community. It's a big decision that can take place now or never. Making a blogpost discussing both sides and asking community to contribute to this issue would allow for bigger variety of opinions. Remeber that by "not making" decision you are in fact "making decision" to keep things so you are already taking one side of divisive argument regardless of how much you want to remain neutral.
4.0 is one in a decade opportunity to bring huge changes like this and it's worth utilising it to it's full potential
We don't take decisions based solely on the discourse on a given proposal, nor on the results of Twitter polls, or any other kind of "global" community consultation.
@akien-mga That's good if it is not just decided by who holds majority or shouts the loudest. Maybe design goals behind GDScript need clarification then. For example, we get to a point where we start to realize that comparing GDScript and Python gives a wrong impression, but reduz then says something like "GDScript tends to be Python-like", which opens another can of worms. I'm pretty sure he meant "historically", but it can also be read as in "going forward". Ultimately, community has no idea what GDScript wants to be, and therefore it argues about everything.
@Feniks-Gaming You are being way too dramatic with "not making a decision is a huge decision" line of thought. With everything in this argument/proposal having even and equal support not breaking things is a better option because it leaves you with the same devil you've always lived with. Breaking things at this point would just turn the tables and replace one devil with another, unknown one.
They are not picking a side in the argument. They are picking a side of not breaking existing things. Big difference.
reply to https://github.com/godotengine/godot-proposals/issues/1866#issuecomment-733661019
@dalexeev I think you are slightly wrong about @reduz (correct me if I'm wrong) as he desires to give the best possible tools to the community, and the reason he is asking the community is to get more information, to make a better informed decision, and the information he got back is very far from decisive for or against as it's very much tied, and as such does what he thinks is the best thing to do at the moment, which is to not implement the change (due to current data being tied) which is a logical move (no extra work, no compat breaking, and goes with what his personal & expert opinion is telling him) while still keeping the change as an option should it end up that he is wrong about it and it actually ending up as a desired feature.
It's not avoiding responsibility, it is in fact being very responsible and logical, and is in fact far more open minded compared to what most other people in his position would do and that is a trait few people posses.
reply to https://github.com/godotengine/godot-proposals/issues/1866#issuecomment-733664871
As far as the poll stuff goes @pycbouh I agree with what you said and just taking the direct question would be harmful to the result and wouldn't end up with the best path.. it's part of the reason for the background data and such, to be able to get an idea of the personalities you are dealing with, what experience they might have, and use that data to better look from their perspectives, with the knowledge that You posses that they might not (as the authority/expert figuring out the best action)...
The Objective opinion part, I never said you needed an Objective Opinion, just that you need data, as without it you're as good as blind.. and you do need to understand your domain (both parts) and as such the only way to get the people part is to survey them somehow.
Newbies are often confused
new ppl to programming can learn, and putting "useful info that saves lots of time in future" to place where they can see this "info", this is the right way
creating lots of "hidden features" is always wrong
and ye I hate when "newbie" presented as "person that unable to learn"
Personally, as someone who came new into programming not too long ago, I would prefer if
5*2.0
5/2.0
and
5 == 5.0
would just through an error saying something along the lines of
"Don't compare apples with oranges. Try '5/int(2.0)' or 'float(5)/2.0' "
much less confusion, much less error prone, much more consistent (currently you can't do "5"/2 either).
@mrjustaguy Unfortunately @reduz is clearly biased towards this proposal.
12:59 Akien vnen: Groud: I didn't follow the flamewar proposal about it, but I feel
#43815 is a compelling argument in favor of adding a `//` operator for
integer division like Python has. If we're raising warnings when people
actually use integer division with `/`, that's a sign that we don't want
them to and therefore a clear separation of responsibility
12:59 Akien might be better.
12:59 IssueBot #43815: Multiple same warning for 1 line script, need to place multiple same
ignore | git.io/JkXxh
13:00 Groud Akien: To be honest, both solutions are fine to me, it's just a matter of
convention I'd say
13:01 Groud As I believe this is kind of a cosmetic change, I think i would simply make
a poll for it
13:02 Groud Akien: ah but, yeah. If we added a warning, it's clear that a separation of
responsibilities seems better
13:04 reduz Akien: yeah does not make a lot of sense to me, I think the problem is
likely somewhere else
13:48 vnen Akien: I do see merit in the // proposal, but I'm not sure about accepting a
proposal with more downvotes than upvotes (even though my guess is that it
wouldn't make a difference to most people)
13:49 reduz vnen: Even though python does it this way, I honestly see its pointless. IMO
casting is more straightforward.
13:50 reduz to a lot of users this is a hassle, which is why the downvote
13:51 Akien reduz: I don't see why this become a hassle though
13:52 Akien You'd only use `//` when you need it to, which is very rare.
13:52 vnen reduz: the merit is in the case where it doesn't matter if it's int or
float, you just want the accurate result (not always you know the type,
given the duck-typing practices)
13:52 Akien Currently if you need integer division and use `/` for that, you get a
warning.
13:52 Akien Adding `//` actually *removes* the hassle of having to do casting.
13:53 Akien reduz: you can see the issue that spawned that discussion - it's good old
super-experienced fracteed who got bit by this: godotengine/godot #43711
13:53 Akien Namely: godotengine/godot #43711#issuecomment-731041963
13:53 reduz Akien: yes I know, what I mean is that assuming / will always return float
is a hassle
13:53 Akien Viewport.size is Vector2i in master, so `Viewport.size.y / Viewport.size.x`
to get aspect ratio doesn't work.
13:54 reduz which is fine its integers
13:54 reduz for a whole lot of people, me included, doing int / int = float sucks
13:55 reduz I also dislike this from python 3 and feel it was completely unecesary
13:55 Akien How often do you do `int / int` while expecting an integer division?
13:55 Akien GDScript throws a warning if you do.
13:55 reduz a lot
13:56 vnen the warning is there because often it's an oversight (like the example in
the issue)
13:57 reduz vnen: warning is ok, because it will be an oversight, but you can disable it
and thats it
13:58 reduz vnen: it can happen either way
13:58 Akien Well instead of disabling the warning you could do `//` and make the intent
explicit :)
13:58 Akien The only argument I see against having `//` is wanting to keep the return
type of a plain division as int
13:59 reduz Akien: It's the same thing either way, you can hit a bug dividing two
integers and getting a float unexpectedly, or dividing two integers and
getting an integer unexpectedly. Because of this, the warning on integer
division will not go away anyway.
13:59 reduz so it's an unnecesary change
14:00 reduz imo the warning is fine, and things should not change as they are now
14:03 Akien The warning also happens only if you use type hints, otherwise it doesn't
14:03 reduz yeah, again, imo its ok as it is, you can get problems either way but the
current way is the most expected one
14:04 reduz I think python derailed on this one
14:04 reduz you know I will always be on the pragmatist side vs the purist side :P
14:05 Akien Yes, but the programmatic side here IMO is that one operation == one
operator.
14:05 Akien It's confusing to many that the / operator can give them int or float
depending on whether it's int/int (int) or float/float, float/int or
int/float (float)
14:06 Akien I don't think it's particularly confusing on the other hand to have `/`
always give float and `//` always give int.
14:06 reduz yes but then you run into inconsistency with the other operators
14:06 Akien It makes the code communicate clearly its intent. Sure it can mean that if
you're a C programmer you might get it wrong initially, but you'll learn,
just like C programmers can learn how to do Python, JS or whatnot.
14:07 Akien var hours = 1337
14:07 Akien var days = hours / 24
14:07 Akien What does the programmer want here? Number of full days past or fractional
number?
14:07 reduz Akien: again, look at the downvotes on it, a lot more people will be annoyed
than happy
14:07 reduz and I can see why
14:07 Akien Change resistance ;)
14:08 reduz I see it more as a change for no reason resistance
14:08 Akien The arguments given in the thread are "it's ugly" and "we're not python"
14:08 reduz yeah, which is fine
14:08 reduz feel free to do a poll if you want
14:08 Akien No it's not fine, those are not valid arguments to refute a valid proposal.
Arguments should be technical.
14:08 reduz if most people favors this, I am ok, otherwise it needs to stay as is
14:09 Calinou personally, I would welcome this change
14:09 Akien Your argument on not wanting int/int to give you a float is valid on the
other hand.
14:09 Calinou I made my comment in that proposal mainly in an attempt to defuse it
14:10 Akien But yeah a poll might be worth a shot.
14:10 Akien If it ends up with 50/50, no change :)
14:14 reduz here: twitter.com/reduzio/status/1331239416199860224
14:16 Akien That's a very biased way to present it :)
14:16 Akien `int/int == float` sounds naturally wrong, while `5/2 == 2.5` sounds
naturally correct.
14:16 reduz oh come on, it's a pretty neutral way, I am not favoring any of them and
Twitter won't let me write much anyway
14:16 reduz Akien: because it is naturally wrong for many
14:16 vnen there's 73 votes already !
14:21 reduz also I would expect most to be familiarwith this topic
14:22 reduz so even if there is a little bias, given the results it probably does not
matter as much
14:24 reduz I don't personally see either way as better than the other, because most can
lead to mistakes, so to me the argument of not wanting to change for no
reason to be valid, even if you have not used the other system.
14:24 vnen my main problem with two operators is that ints and floats behave
differently so you need to control when the conversion happens if we want it
14:26 vnen we can add a '//' operator that always do float division :P
14:28 reduz I think pascal was kind of like this too right? they have the div operator
which was always integer
14:28 Calinou yeah: wiki.freepascal.org/Div
14:29 reduz yeah i figured, I had the vague memory of being confused by c style operator
when I moved from Pascal to C
14:29 reduz I was like, where is the div operator?
14:29 Calinou fun fact: the Godot repository now includes Pascal code :P
14:29 Calinou (added by godotengine/godot #42863)
14:29 reduz heh nice
14:32 reduz I wonder if the Python people made a poll or they just went with it :P
14:32 Groud We can't edit a vote on a twitter poll ?
14:32 Groud That's weird...
14:34 reduz vnen: my worry is also that many users won't even know there is an //
operator, as evidenced by this post:
twitter.com/MuktadaCreation/status/1331239972154830848
14:34 reduz vnen: so they will end up casting instead, which is more inefficient and
kinda sucks
14:36 reduz vnen: this was also my case when I moved from Python2 to Python3, I had no
idea until I had to look for how to do integer division because suddendly
the code did not work. Many users won't even bother and just cast.
14:37 vnen reduz: yeah, that's why I mention deciding when to convert types. If you do
int(int/int) then your value was a float at some point, which might not be
wanted
14:37 reduz vnen: so, even if both approaches have basically the same problem (obtaining
an unexpected result), the current one will lead you to do more efficient
code in the end when you stumble upon it
14:38 reduz vnen: so, for languages with typing (which is the current situatin of
GDScript), I think keeping it as is is better because there is less chance of
implicit conversions happening
14:39 Akien Yeah it's a good point that with static typing the current system might be
preferred.
14:40 Akien Let's keep in mind that Python 3 only introduced type hints in 3.6 - so when
they chose to add //, they were operation under full duck typing premises.
14:40 Akien *operating
14:40 Akien So far C nerds are leading, but don't discount the progressive mail ballots
:P
14:57 Akien Told you so :P twitter.com/lukostello/status/1331249441622388737
14:08 Akien The arguments given in the thread are "it's ugly" and "we're not python"
Well, that's not fair, Remi. My initial response was somewhat technical: I don't want coercion, I like that GDScript doesn't assume much about the intentions of my code and leaves the control to me. The arguments like "we are not python" were only thrown in response to the similar arguments in favor of the change ("Python did it, so must we because GDScript is basically Python"). That's just as invalid and not fine and I've said just as much several days ago. And for example, user "bluenote10" put a lot more effort into their argumentation for the change. Same cannot be said about the most of this discussion.
As for proposed alternative syntax, while "it's ugly" sounds vain and superficial there were other comments saying that the syntax is confusing, unintuitive and collapses with a syntax for line comments for people who have experience with other programming languages. Which are valid opinions on the syntax and should be considered. I can also add that I don't welcome an idea of a special operator that is needed for one specific purpose only β division of two ints. That doesn't look like a valuable addition to the grammar if it solves only one very particular problem. That's more of a hack to work around a limitation, similar to === in JavaScript.
The problem can be extended to other types as well. I'm pretty sure many new programmers expect being able to concatenate strings and numbers. We do not allow it currently. Not in stable at least.
var text1 = "test"
var int1 = 2
var res = text1 + int1 # Some would expect "test2" as a string output
This throws Invalid operands 'String' and 'int' in operator '+'.. Should we support it? Do we want to go this way with GDScript? This is the important question here, in my opinion. We should not introduce coercion in a vacuum. We should not introduce operators useful for one very specific case. This is a dangerous route and such changes pollute the language which we aim to keep simple.
I've Read the IRC and here's a few things I've got to say:
14:08 reduz I see it more as a change for no reason resistance13:49 reduz vnen: Even though python does it this way, I honestly see its pointless. IMO
casting is more straightforward.13:59 reduz Akien: It's the same thing either way, you can hit a bug dividing two
integers and getting a float unexpectedly, or dividing two integers and
getting an integer unexpectedly. Because of this, the warning on integer
division will not go away anyway.13:59 reduz so it's an unnecesary change14:05 Akien It's confusing to many that the / operator can give them int or float
depending on whether it's int/int (int) or float/float, float/int or
int/float (float)14:08 Akien No it's not fine, those are not valid arguments to refute a valid proposal.
Arguments should be technical.14:08 reduz if most people favors this, I am ok, otherwise it needs to stay as is14:16 Akien "int/int" == float sounds naturally wrong, while "5/2 == 2.5" sounds
naturally correct.14:21 reduz also I would expect most to be familiarwith this topic14:24 reduz I don't personally see either way as better than the other, because most can
lead to mistakes, so to me the argument of not wanting to change for no
reason to be valid, even if you have not used the other system.14:34 reduz vnen: my worry is also that many users won't even know there is an //
operator, as evidenced by this post:
twitter.com/MuktadaCreation/status/133123997215483084814:37 reduz vnen: so, even if both approaches have basically the same problem (obtaining
an unexpected result), the current one will lead you to do more efficient
code in the end when you stumble upon itI don't welcome an idea of a special operator that is needed for one specific purpose only β division of two ints.
The fact is that you can divide an integer by an integer in different ways. If you have 5 apples, you can give 2 to one, 2 to another, and discard the remaining apple. Or you can go ahead, take a knife and cut this apple. These are two different ways, two different operations. This has been written above, many times. Also see my example with <unsigned char> + <unsigned char> != <unsigned char>. Proponents of C behavior have too many myths.
@dalexeev mathematically it is actually accurate to have 5 apples and split them 2 and discard 1.. IF the results must be an element Z, so to give a better example, you Have 5 Buttons, and you want both shirts to have the same number of buttons, you give 2 buttons to one, and 2 to the other, and discard the remaining 1, because half a button on a shirt is pointless.
@dalexeev I just don't welcome such specific operator. If this proposal was to be implemented and the default behavior of int / int was to be changed, I'd prefer we have a function like was suggested by someone earlier, not an operator.
We have a precedent of this already, we have fmod because % only operates on ints and nobody is batting an eye.
@mrjustaguy The main point of this proposal is to specify explicitly each time which division you need, full (float) or partial (int). If you don't like / and // then we can introduce / and div. But the essence does not change: there are two different operations. Pascal (statically typed) and Python (dynamically typed) agree on this.
@pycbouh So div(a, b) is OK?
So
div(a, b)is OK?
Would be idiv() probably to go along with fmod, but yes. I'm not a proponent of this change, but if it was to happen regardless, better we stick it into a separate method rather than an obscure operator.
Alternatively, we can see if that operator can be useful in some other case. For example, should % work with floats, and for the current behavior we introduce /%? That would go along with the / and // parity. That would make some sense syntax-wise. It would still be as ugly as the constant use of === in JavaScript if you want your code to be stable and of a high quality, but at least it won't be an isolated operator burdening the grammar.
I would be much more ok with div(a, b) because it's not changing how / works.
But the essence does not change: there are two different operations.
So is dividing int with ints and floats with either int or float.
I was going to write something longer, quoting and answering some points in detail, but I'm on my lunch time and I don't think this should even be a thing.
If you're using floats in division, you explicitly want to have a float result. The same goes to integer, this isn't difficult to anyone to understand. I don't think in newbies are often confused as an argument. Newbies will become confused of most of the things depending on their backgrounds, and most of the confusion goes away in a few minutes. Also, the static typing syntax is here to be used. Want to be warned when trying to assign 5 / 2.0 to a int? You'll be well served.
GDScript is not Python. The main purpose of the language is to provide a nice abstraction to work with your games. Most of the examples said here have nothing to do with real cases. Want a new operator? The entire source of the engine is here, fork it, throw AppVeyor/Travis and get your build as you want. I'm sorry if I'm looking aggressive in the way I'm expressing this, but it's just that this discussion is not productive. It's not something that will be added (at least for now), as already answered, and to be honest... This doesn't improve the development process, sounds like a useless discussion.
In the end of the day, isn't the good code telling explicitly what you want?
var result: float = 5 / 2.0
var other_result: float = 5 / float(2)
This looks much nicer than a // operator. Can you say me some real examples where a game would need it?
#include <stdio.h>
int main() {
unsigned char a = 255;
unsigned char b = 255;
unsigned char c = a + b;
printf("%d\n", a + b); // 510
printf("%d\n", c); // 254
}
For C lovers who say "just cast one argument to float", I will ask you: would you like it to work like this:
#include <stdio.h>
int main() {
unsigned char a = 255;
unsigned char b = 255;
unsigned char c = a + b;
int d = a + b;
int e = (int) a + b;
printf("%d\n", a + b); // 254
printf("%d\n", c); // 254
printf("%d\n", d); // 254
printf("%d\n", e); // 510
printf("%d\n", (int) a + b); // 510
}
This is how it works with your "logical" <int> / <int> == <int>.
Personally I'd prefer the 2nd..
Reasoning:
unsigned char is from 0 to 255, when it get out of the range, it moves by what it's out simply put.
d gives an int with the value of c, which makes sense as it does the operation, overflows, ends up where it ends up, and stores that in an int.
If I want the total value, so e in your example, I just turn a and/or b into int so that it doesn't overflow.
Please note however I'm not a C user, I'm horrible with C and it's derivatives, partly why I use GDScript over C#
@mrjustaguy The operator should not make assumptions about where its result will be used and whether it will fit into the range or not. The operator must calculate the result as accurately as possible and pass the value on. In C, the addition operator works like this, but the division operator works differently (for historical reasons). People are used to it and find the way to the only trueβ’. They don't even notice this inconsistency in the language.
@dalexeev I feel like people notice, they just don't dwell on it and they adapt.
@Wykleph It is impossible to keep everything in your head. I, a few people here and a few in the titter sometimes forget about this behavior and get bugs. No wonder. Tell me, do you enter 5.0/2 or 5/2 in your smartphone's calculator?
(I won't fix the typo about twitter.)
if you need to adapt then the language isn't intuitive.
Imagine you come across this code sniped.
var hours_passed = min_passed/60
Can you seriously put a hand on a heart and swear to me that you are 100% certain that the intention of a person is to get int and can you be 100% certain that the intention of this code is for
var min_passed = 75
var hours_passed = min_passed/60
to return 1 not 1.25?
Under new system code is more easy to ready
var hours_passed = min_passed/60 # I know fellow coder wants only full hours passed.
var hours_passed = min_passed//60 # I know fellow coder wants hours and some left over passed.
There is no ambiguity. Code is more intentional that way.
Any argument against this is mostly "we are used to it that way".
I think many here are way too emotionally invested in this discussion, so I'll lock it for a few days so we can all take time to chill.
Sorry guys, I explained it as best as possible, so please try to understand it.
The fact we are doing things this way already (with little to no complaining), and that there is strong opposition to changing it from a significant share of the users and core contributors (lack of consensus) makes this a no-go for the time being. Discussion on technical merit or whether users would adapt is irrelevant at this point.
Most helpful comment
Understandable, but the problem is that casual followers can take a somewhat shallow look on such topics. Anyone who is willing to dive into the motivation given e.g. by Guido van Rossum (or study the design of other dynamically typed languages that have e.g. opted for
/anddivfor the same reason) should realize that the reasoning is sound and there is general consensus that one division operator works well in statically typed languages, but dynamically typed languages are generally better off separating them into two to rule out a common source of bugs. Of course, not everyone has the time to do that. A democratic vote can lead to an inferior overall solution if the vote gets biased by superficial "please no" reactions (please don't take this as an offense @Shadowblitz16, most likely you simply didn't waste too much time on the topic, in which case this is a totally sensible and understandable reaction :heart: ).@mrjustaguy
I just wanted to point out that the work-around actually isn't that straightforward. Casting to
floatis technically not entirely correct. Imagine a functionnormalize_values(values, reference_value)that takes an array of values and normalizes all values with respect toreference_value, i.e., it returns a new array with all values transformed asvalue / reference_value:The interesting thing is that such a function can be "generic" (= uses duck typing), i.e., it can operate on anything like:
In the current form, the function contains the infamous division bug
value / reference_valuewhich gives the wrong result in case (1).Using the often mentioned work-around
float(value) / reference_valuemakes (1) and (2) work, but actually prevents proper duck-typing, i.e., it would break (3) and (4), becausefloat(Vector2(1, 1))doesn't work.A technically correct work-around would be to use the expression
value * 1.0 / reference_valuebecause it is the only operation that is fully duck-typing friendly.Such duck typing use cases are arguably rare, but just pointing out that the work-around has some subtleties -- and in fact the "best" work-around hasn't even been mentioned.
While I still think that this proposal would be the best option I also have an idea that would be fully backwards compatible while providing a means of preventing the most common forms of the division bug. I may write an alternative proposal if I find the time.