Android Question How to correctly compare values? (get expected results and avoid crashes)

Sandman

Expert
Licensed User
Longtime User
(This thread is a spin-off from https://www.b4x.com/android/forum/threads/1-1.117065/, which is a really interesting thread that I encourage people to read in full.)

I mean, if the compiler sees a comparison where one side is an object and the other is not, and its safer to put it on the right side of the comparison - couldn't the compiler just switch places so that the object is always is on the right side?

It is not possible as it will have other side effects. The left side type has semantic meaning.
B4X:
Log("a" = 1) 'string comparison ("a" = "1") = false
Log(1 = "a") 'numeric comparison => crash

I feel very naïve here, but I'm also learning a lot so I feel I have to ask a couple of follow-ups.

1. You're sort of saying that the left side decides how the comparison should happen, and that the right side is cast'd to same type as left side, is that correct?

2. In your example above, is either "a" or 1 considered an object? Because if they're not, perhaps that example isn't relevant..?

3. I suspect this would cause a lot of problems that I can't foresee, but I have to ask: If both sides of the equality comparison are different types, wouldn't it make sense to just consider them different and return false, instead of crashing?
 
Solution
1. You're sort of saying that the left side decides how the comparison should happen, and that the right side is cast'd to same type as left side, is that correct?
Yes, it is more than casting. If it is a numerical comparison then the right side will be "converted" to a number and vice versa.

2. You asked in the other thread, why doesn't the compiler switches the right side and left side when the right side is an object.
I've tried to explain that it will change the program behavior and not always as you expect.

3. No. B4X compiler converts the types for you when it can (exactly the opposite of what @MrKims claimed here: https://www.b4x.com/android/forum/threads/1-1.117065/post-731902).
You don't need to convert numbers...

Erel

B4X founder
Staff member
Licensed User
Longtime User
1. You're sort of saying that the left side decides how the comparison should happen, and that the right side is cast'd to same type as left side, is that correct?
Yes, it is more than casting. If it is a numerical comparison then the right side will be "converted" to a number and vice versa.

2. You asked in the other thread, why doesn't the compiler switches the right side and left side when the right side is an object.
I've tried to explain that it will change the program behavior and not always as you expect.

3. No. B4X compiler converts the types for you when it can (exactly the opposite of what @MrKims claimed here: https://www.b4x.com/android/forum/threads/1-1.117065/post-731902).
You don't need to convert numbers to strings and vice versa.
B4X:
Dim x As Int = File.ReadString(...) 'valid code
File.WriteString(..., x) 'also valid code
If x = Map.Get("NumberOfItems") Then 'also valid
Dim x As Int = EditText1.Text 'valid but can be unsafe.
Label1.Text = "Number of items: " & x 'also valid
 
Upvote 0
Solution

Erel

B4X founder
Staff member
Licensed User
Longtime User
There are edge cases which can happen when the compiler cannot know at compile time the type of the left side value.

For example:
B4X:
Dim d As Double = 1
Dim o As Object = d
Log(d = 1) 'true
Log(o = 1) 'false
Log(1 = o) 'true
Dim i As Int = 1
Dim o As Object = i
Log(o = 1) 'true

In line #4 the compiler doesn't know that it is comparing numbers and it will treat the values as not equal because their type is different (double and int).

The bottom line is that you should be careful when the left side type is an Object.
B4X:
If Map.Get("key") = 3 Then 'this will work as long as the value type in the map is Int
'Safer:
If 3 = Map.Get("Key") Then
'or
Dim value As Int = Map.Get("Key")
If value = 3 Then
 
Upvote 0

OliverA

Expert
Licensed User
Longtime User
2. You asked in the other thread, why doesn't the compiler switches the right side and left side when the right side is an object.
I've tried to explain that it will change the program behavior and not always as you expect.
A convoluted example on why order matters. Both a and b are global variables, where a is declared as a String (an object) and b as an Int (a primitive)
B4X:
Sub test
    a = "aString"
    b = 4
    
    If testA = testB Then ' testA returns an object.
        Log($"${a} equals ${b}"$)
    Else
        Log($"${a} does not equal ${b}"$)
    End If

    a = "aString"
    b = 4

    If testB = testA Then ' Let's put testA on the right.
        Log($"${a} equals ${b}"$)
    Else
        Log($"${a} does not equal ${b}"$)
    End If
End Sub

Sub testA As String 
    a = Power(b, 2)
    Return a
End Sub

Sub testB As Int
    b = b * b
    Return b
End Sub

Log output:
16 equals 16
256 does not equal 16
 
Upvote 0

Sandman

Expert
Licensed User
Longtime User
The bottom line is that you should be careful when the left side type is an Object.
That's a very clear bottom line, and I've never heard it before. I'm not sure if I've missed something obvious somewhere, but perhaps it could be clearer? Perhaps even added to the linter in the IDE?
 
Upvote 0

angel_

Well-Known Member
Licensed User
Longtime User
I use KeyValueStore, is it possible to have the same problem with this?
B4X:
If Starter.kvs.Get("Numero") = 1 then

'or

If Starter.kvs.GetDefault("Numero", 1) = 1 then
 
Upvote 0

Erel

B4X founder
Staff member
Licensed User
Longtime User
Perhaps even added to the linter in the IDE?
Good suggestion.

I use KeyValueStore, is it possible to have the same problem with this?
It doesn't matter where the data comes from. If there is an Object (and there is) on the left side then the types must match.
 
Last edited:
Upvote 0

agraham

Expert
Licensed User
Longtime User
If there is an Object (and there is) on the right side then the types must match.
Do you really mean 'right' and not 'left' because in post #3 above

5 Log(1 = o) 'true

the types appear to not match (int = double) but the comparison works. Or am I missing something :(

EDIT: I wasn't missing anything :) Erel has fixed his typo in the post above..
 
Last edited:
Upvote 0

angel_

Well-Known Member
Licensed User
Longtime User
It doesn't matter where the data comes from. If there is an Object (and there is) on the right side then the types must match.
In B4A this kvs.GetDefault("BooleanVariable", True) return True or False, but en B4i return 1 or 0.

I have this:
B4A:
If kvs.GetDefault("BooleanVariable", True) then

But in B4i
B4i:
If kvs.GetDefault("BooleanVariable", True) then 'is it correct?

'or I have to use:
If 1 = kvs.GetDefault("BooleanVariable", True) then
 
Upvote 0
Top