Why Dart holds *double standard* between `==` and `>=` - dart

I have two int values in Dart, test them equals or not with == previously, it was fine. Now I have to test if first one is greater or equals to the second one, so I changed == to >= instinctively, but it seems Dart was not happy with me doing that
I double checked documentation of Dart, >= should work to test greater or equals operation
IMO, in the end, if >= has something wrong, then the same should come to ==

You are comparing two different operators which comes from different part of the Dart language. The == operator is defined on Object:
https://api.dart.dev/stable/2.15.1/dart-core/Object/operator_equals.html
Which needs to be able to compare every type of object including null. The reason why the == operator does not have Object? as input parameter is because of the following detail in the Dart Language Specification (page 167) under the section "Equality":
Evaluation of an equality expression ee of the form e1 == e2 proceeds as follows:
The expression e1 is evaluated to an object o1.
The expression e2 is evaluated to an object o2.
If either o1 or o2 is the null object (17.4), then ee evaluates to true if both o1 and o2 are the null object and to false otherwise. Otherwise,
evaluation of ee is equivalent to the method invocation o1.==(o2)
...
https://dart.dev/guides/language/specifications/DartLangSpec-v2.10.pdf
So based on third rule, the language itself will make sure that null values are compared correctly and therefore the Object.== method will never see a null object as its input.
The other operator is >= and comes (in your case) from num which int extends from:
https://api.dart.dev/stable/2.15.1/dart-core/num/operator_greater_equal.html
And is specified to accept only num (so int or double) which are a non-nullable type (null is therefore not allowed).
You are then trying to call the num.>= operator with the result from values?.length. This is a problem since values is nullable and can therefore potentially be null. And when you use the ?. operator, you are saying that if values are null, don't care calling .lenght but instead just return null.
So the type of values?.length ends up being int? which is not compatible with num.

Related

Lua nonsense to find if something is nil

There seems to be a difference between these two checks:
if not object then
if type(object) == "nil" then
However I really don't understand the difference.
if type is "nil", shouldn't a not object then also work?
Lua, like many other dynamically typed languages, has the concept of "truthy" and "falsy", where boolean expressions can handle more values than just actual booleans.
Each non-boolean value has a specific meaning attached when used in a boolean expression. Specifically:
nil and false are "falsy"
everything else is "truthy"
That is why (not nil) == (not false), but type(nil) ~= type(false), because not x is a boolean expression that coerces x to truthy/falsy, while type() checks the actual type.

confused about the null safety dart

The argument type 'int?' can't be assigned to the parameter type 'num'.
when using darts with 'null safety', this code gets an error but when I use the version of darts without null safety the test runs successfully (without error). My question is how do I change this code so that it doesn't error on darts with null safety?
If you take a look at the documentation of the [] operator on Map:
V? operator [](
Object? key
)
V? here means that the type of the value V will always be nullable. The reason for this is because of the following detail for this operator:
The value for the given key, or null if key is not in the map.
https://api.dart.dev/stable/2.18.1/dart-core/Map/operator_get.html
So the reason for your error is because your code is not prepared for the case of [] returns null in case the key are not in your Map.
There are multiple solutions for this problem which depends on how sure you are about the specific piece of code and what behavior you want in case of failure.
If you are e.g. completely sure that curr will always point to a valid key in the Map you can do: scarScore[curr]! (notice the !). This will add a runtime null-check and crash your application in case the value from the [] operator ends up being null.
Alternative, if you expect some keys to not be in the Map, you can provide default values like prev + (scarScore[curr] ?? 0) where null will then be assumed to be translated to the value 0.
For a detailed explanation for why the [] operator are nullable, you can read: https://dart.dev/null-safety/understanding-null-safety#the-map-index-operator-is-nullable

How check type reference - dart

If I have a object like this:
class MyClass<K>{...
How I could check type of K? If was a variable was easy,
ex:
(myVar is Object)... //true | false
But in my case, this dont works:
(K is Object) // awalys false
You want == here. Using is is for comparing the type of variable, not a literal type.
This will only check if K is actually Object if you use K == Object. If you pass K as int for instance, it will not be considered to be an Object.
I recommend not using == for comparison of Type objects. It only checks exact equality, which might be useful in a few situations, but in plenty of situations you do want a subtype check.
You can get that subtype check using a helper function like:
bool isSubtype<S, T>() => <S>[] is List<T>;
Then you can check either isSubtype<K, Object> to check if K is a subtype of Object (which is true for everything except Null and types equivalent to Object?, but that can also be checked like null is! K), or isSubtype<Object, K> to check whether K is a supertype of Object.
It has the advantage that you can compare to any type, not just types you can write literals for. For example K == List only works for List<dynamic>. If you need to check whether K is a List<int>, you can do isSubtype<K, List<int>>.
You can get equivalence of types (mutual subtype), without requiring them to be the same type, by doing isSubtype in both directions. For example isSubtype<Object?, dynamic>() and isSubtype<dynamic, Object?>() are both true, but if K is Object? then K == dynamic is false.

Clarification on avoid_null_checks_in_equality_operators linter rule

There is a linter rule which verifies that one don't check for null equality in the overridden == operator.
The rule is here.
I understand this rule but can't see how it is realized technically.
It seems that Dart itself makes some implicit check on other != null and == returns false in this case. Is this correct?
In other languages, e.g. Java, one needs to explicitly add this check in the overridden equals.
Second question is why then it does not check automatically on the type of other as well. Why it is ok to spare me as a programmer from checking on null, but I still need to check if other is Person? Are there cases when one overrides == and checks there for some other type then the type of that class?
The linter rule implementation is simple, it just checks whether you compare the argument of operator == to null.
The reason you don't need to is that e1 == e2 in Dart is defined to first evaluate e1 and e2 to a value, then give a result early if one or both of the values is null, and only otherwise it calls the operator== method on the value of e1.
So, when that method is called, you know for certain that the argument is not null.
The reason the == operator doesn't do more checks before calling the operator== method is that there are examples where that would be wrong.
In particular, int and double can be equal to each other. Having instances of different classes potentially being equal to each other is more common that you'd think (proxies, mocks, wrappers, Cartesian point vs polar point, int vs double).
The other check you could potentially do early is to say that an object is equal to itself, so if (identical(this, other)) return true;, but there is one counter-example forced upon the language: NaN, aka. double.nan. That particular "value" is not equal to itself (which breaks the reflexivity requirement for ==, but is specified that way by the IEEE-754 standard which is what the CPUs implement natively).
If not for NaN, the language would probably have checked for identity before calling operator== too.
It seems that Dart itself makes some implicit check on other != null and == returns false in this case. Is this correct?
Yes.
Second question is why then it does not check automatically on the type of other as well. Why it is ok to spare me as a programmer from checking on null, but I still need to check if other is Person? Are there cases when one overrides == and checks there for some other type then the type of that class?
It's less common, but there can be cases where you might want to allow a different type on the right-hand-side of the equality. For example, the left-hand-side and right-hand-side might be easily convertible to each other or to a common type. Imagine that you created, say, a Complex number class and that you wanted Complex(real: 4.0, imaginary: 0.0) == 4 to be true.
From the doc:
no class can be equivalent to [null]
Meaning, when other is Person is true, then other != null is also true. This is because:
The null object is the sole instance of the built-in class Null.
(https://dart.dev/guides/language/spec)
So every instance check against any type but Null will return false for null:
class A {}
void main() {
final x = null;
final a = A();
print(x is Null); // true
print(x is A); // false
print(a is Null); // false
print(a is A); // true
}

trying witness null: result of operation might violate subset type constraint

I've written a class that represents a binary relation on a set, S, with two fields: that set, S, and a second set of pairs of values drawn from S. The class defines a bunch of properties of relations, such as being single-valued (i.e., being a function, as defined in an "isFunction()" predicate). After the class definition I try to define some subset types. One is meant to define a subtype of these relations that are also actually "functions". It's not working, and it's a bit hard to decode the resulting error codes. Note that the Valid() and isFunction() predicates do declare "reads this;". Any ideas on where I should be looking? Is it that Dafny can't tell that the subset type is inhabited? Is there way to convince it that it is?
type func<T> = f: binRelOnS<T> | f.Valid() && f.isFunction()
[Dafny VSCode] trying witness null: result of operation might violate subset type constraint for 'binRelOnS'
Subset types and non-emptiness
A subset type definition of the form
type MySubset = x: BaseType | RHS(x)
introduces MySubset as a type that stands for those values x of type BaseType that satisfy the boolean expression RHS(x). Since every type in Dafny must be nonempty, there is a proof obligation to show that the type you declared has some member. Dafny may find some candidate values and will try to see if any one of them satisfies RHS. If the candidates don't, you get an error message like the one you saw. Sometimes, the error message will tell you which candidate values Dafny tried.
In your case, the only candidate value that Dafny tried is the value null. As James points out, the value null doesn't even get to first base, because the BaseType in your example is a type of non-null references. If you change binRelOnS<T> to binRelOnS?<T>, then null stands a chance of being a possible witness for showing your subset type to be nonempty.
User-supplied witnesses
Since Dafny is not too clever about coming up with candidate witnesses, you may have to supply one yourself. You do this by adding a witness clause at the end of the declaration. For example:
type OddInt = x: int | x % 2 == 1 witness 73
Since 73 satisfies the RHS constraint x % 2 == 1, Dafny accepts this type. In some programs, it can happen that the witness you have in mind is available only in ghost code. You can then write ghost witness instead of witness, which allows the subsequent expression to be ghost. A ghost witness can be used to convince the Dafny verifier that a type is nonempty, but it does not help the Dafny compiler in initializing variables of that type, so you will still need to initialize such variables yourself.
Using a witness clause, you could attempt to supply your own witness using your original definition of the subset type func. However, a witness clause takes an expression, not a statement, which means you are not able to use new. If you don't care about compiling your program and you're willing to trust yourself about the existence of the witness, you can declare a body-less function that promises to return a suitable witness:
type MySubset = x: BaseType | RHS(x) ghost witness MySubsetWitness()
function MySubsetWitness(): BaseType
ensures RHS(MySubsetWitness())
You'll either need ghost witness or function method. The function MySubsetWitness will forever be left without a body, so there's room for you to make a mistake about some value satisfying RHS.
The witness clause was introduced in Dafny version 2.0.0. The 2.0.0 release notes mention this, but apparently don't give much of an explanation. If you want to see more examples of witness, search for that keyword in the Dafny test suite.
Subset types of classes
In your example, if you change the base type to be a possibly-null reference type:
type func<T> = f: binRelOnS?<T> | f.Valid() && f.isFunction()
then your next problem will be that the RHS dereferences f. You can fix this by weakening your subset-type constraint as follows:
type func<T> = f: binRelOnS?<T> | f != null ==> f.Valid() && f.isFunction()
Now comes the part that may be a deal breaker. Subset types cannot depend on mutable state. This is because types are a very static notion (unlike specifications, which often depend on the state). It would be a disaster if a value could satisfy a type one moment and then, after some state change in the program, not satisfy the type. (Indeed, almost all type systems with subset/refinement/dependent types are for functional languages.) So, if your Valid or isFunction predicates have a reads clause, then you cannot define func in the way you have hoped. But, as long as both Valid and isFunction depend only on the values of const fields in the class, then no reads clause is needed and you are all set.
Rustan

Resources