Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dart thinks a function returns num not int. #2741

Closed
brilliapps opened this issue Dec 23, 2022 · 8 comments
Closed

Dart thinks a function returns num not int. #2741

brilliapps opened this issue Dec 23, 2022 · 8 comments
Labels
request Requests to resolve a particular developer problem state-rejected This will not be worked on

Comments

@brilliapps
Copy link

brilliapps commented Dec 23, 2022

Let code speaks for itself

typedef testfunction = Function;
int funcfun() {
  int erwre = 20;
  return erwre;
}

testfunction abcdgcccc = funcfun;

and then in main function:

// not causing error
int tratata = 10 + funcfun();

// causing error
int tretete = 10 + abcdgcccc();

Why there is error for "tretete" variable, while it returns int and has defined return type int

The error message:
lib/main.dart:115:20: Error: A value of type 'num' can't be assigned to a variable of type 'int'.
  int tretete = 10 + abcdgcccc();

It seems to me there is issue whit dart - dart probably should know the return type is int.

By the way i need it working for StringOrInt pseudo "Type", which in reality is a simple function Type returning int or String when invoked.
But if it may return num not int like i mentioned before i have some trouble.
The types i define just like that and this is to tell you what type is accepted:

typedef StringOrInt = Function;
typedef Types<V, W> = Function;

String test_method(Types<int, String> string_or_int) {
  return 'abc'; // doesn't make sense, just it is to return enything.
}

Would be nice if there were types accepting values of one of several types (like FutureOr<T>), or a self invoking function without parentheses ( ) (abc = 10 + function_name_without_parentheses_invokation) i could use f.e. in arythmetic operations like those in the code above.
Also when i have a non int object i can write/override a "+" operator and do int abc = my_non_int_object + 10; - and it works. But i cannot change the order without error: int abc = 10 + my_non_int_object ; - error - there is no operator for situation my_non_int_object to the right. Also related to these problems is that i cannot write two functions with the same name each, and one accepting int param and the second string. So not even one solution to two- or multi-types :)

Is there any simple solution to these things? Is there something planned?

@brilliapps brilliapps added the request Requests to resolve a particular developer problem label Dec 23, 2022
@lrhn
Copy link
Member

lrhn commented Dec 24, 2022

Dart's + operator returns num, so if there hadn't been language-level magic to help you, int tratata = 10 + funcfun(); should have failed because num is not assignable to int (after null safety removed implicit downcasts).

There is language-level magic to help you with this problem.
The static type of 10 + x depends on the type of x. If x is int, the result is also int. If the static type of x is not int, then it might be a double, in which case the result type is double, or it can be a num, and then we can't know whether it's a double or an int, which will give results of type double and int respectively, so the static result type must be num.

In the 10 + funcfun() case, the static type of funcfun() is int, so the result is int.

In the 10 + abcdgcccc() case, the static type of acdgcccc() is dynamic (the return type assumed for dynamic function invocations, including Function invocations). That's not int, so the static type of 10 + abcdgcccc() is num, and num is not assignable to int. The error is warranted, because the compiler cannot statically ensure that the result will be an int, even if it turns out to be one at runtime.
There is no way to statically know that the Function returns an int, so assuming the result will be an int is not sound.

As for types accepting multiple types of values, that would be union types, #1222. Adding such is not on the list of features currently being worked on.

@lrhn lrhn added the state-rejected This will not be worked on label Dec 24, 2022
@lrhn lrhn closed this as completed Dec 24, 2022
@brilliapps
Copy link
Author

This explains everything. Thank you for you time and clear explanation!

@brilliapps
Copy link
Author

For informational purposes. I've just been able to achieve the bare minimum i wanted (for now of course i do very profisonal code, i am just checking out available options),
and just corrected to this:

var abcdgcccc = funcfun;

dynamic test_method(Types<int, String> string_or_int) {
return string_or_int();
}

So later i will make sure, but see i got the very least i wanted, a FEELING of two types or multitypes type, I write this because someone might want to know he/she could do this this way. And if i could remove the ( ) paretheses string_or_int - it might be more than a 'FEELING'. See you.

@brilliapps
Copy link
Author

brilliapps commented Dec 24, 2022

Update, i said about parentheses. I just didn't see such a simple solution like this:


typedef Types<V, W> = dynamic;

//you remember:
String test_method(Types<int, String> string_or_int) {
  return 'abc';
}

Code analyser in Visual Studio Code doesn't seem to be offended by this.
Yeah, it's dynamic type and still - need to do some value or type checking in the function body, but
"Types<int, String>" speak for itself, looks nice, [EDIT:] and when you mouse over the type of the param does give some additional info in Visual Studio Code by reading /// doc comments but i couldn't see for the test_method right info for the param in the simple "dart doc" created documentation, where "Types<int, String> string_or_int" is translated to "dynamic string_or_int", however there is in the doc the "typedef Types<V, W> = dynamic" with /// doc-comment description - at least this.
So the Types type, as function param, you can only see in Visual Studio Code.
As i said before this is the bare minimum needed for me.

@brilliapps

This comment was marked as duplicate.

@brilliapps
Copy link
Author

brilliapps commented Dec 30, 2022

Not much tested i can improve it later. The closest and simplest i could get to union types, multitypes, twotypes (doubletypes) after much time of research and tests - as an example two types V means Value of a type, W means value of a type.
You need to use type TypesV constructor for first type value, TypesW for second. You can change value of a variable is stored in the created object this way but must be of the original type. It seems to be much resilient with regard to using it not strictly as examples point. The need for Types class to be abstract and extended is because as you see for example V may not be null, but in the constructor it is forced to be allowed to be null as an option. So without further explanation this problem could be solved only using the two classess extending the class Types - one for the first type parameter, the second for the sedond.

abstract class Types<V, W> {
  Types({V? v, W? w});
}

class TypesV<V, W> extends Types<V, W> {
  V v;
  TypesV(V this.v) : super(v: v);
}

class TypesW<V, W> extends Types<V, W> {
  W v;
  TypesW(W this.v) : super(w: v);
}

//And that's it. Out of it i guess you can make three-types (Types3<V, W, U>) or more specialized type versions.

String test_method(Types<int, String> string_or_int) {
  return 'abc';
}

// examples how resilient it is in your Visual Studio Express or during compilation time errors.
Types<int, String> abcty = TypesV<int, String>(10);
Types<int, String> abcde = TypesW<int, String>('qwe');
var abcdek = TypesW<int, String>('qwe');
//error: Types<int, String> abc = TypesV<int, String>(10.4);
//var rtet = test_method(abc);
//error: Types<int, String> abca = TypesV<double, String>(10.4);
//error: Types abcdek = TypesW<int, String>('qwe');
//error:var abcdek = TypesW('qwe');

var rtetde = test_method(abcde);
var rtetdef = test_method(abcty);
var rtetdefp = test_method(abcdek);

It is not such a loophole but you definetely shouldn't use casting (see num and int as a result) like TypesV<num, String>(10) as Types<int, String>. It is not the point, you but normally don't do that, it is not the purpose ot this solution to do some strange casting of the Types class like object.

@lrhn
Copy link
Member

lrhn commented Jan 2, 2023

Class hierarchies are a kind of union types. They can be used to implement explicit choices between values.
The classes just need to be declared first, so two different places in the code which want to have "an A or a B" will need to agree on the type used for that choice, and maybe also which order the types go in.

Unordered structural union types would allow you to write (A | B) in one place and (B | A) in another, and it would still be the same type.

A traditional implementation of a tagged union could be:

abstract class Choice<A, B> {
  factory Choice.a(A value) = ChoiceA;
  factory Choice.b(B value) = ChoiceB;
  ChoiceA<A>? asA() => null;
  ChoiceB<B>? asB() => null;
}
abstract class ChoiceValue<T> {
  T get value;
}
class ChoiceA<A> extends Choice<A, Never> implements ChoiceValue<A> {
  final A value;
  ChoiceA(this. value);
  ChoiceA asA() => this;
}
class ChoiceB<B> extends Choice<Never, B> implements ChoiceValue<B> {
  final B value;
  ChoiceB(this. value);
  ChoiceB asB() => this;
}

@brilliapps
Copy link
Author

brilliapps commented Jan 2, 2023

Thanks Irhn, hope don't have a feeling that you have fallen into a trap of answering me :). Need some time to better process your anwer (my own solutions took me much time too and several attempts, i am slow thinking. The current unordered state of it all with unconsistent description is currently here https://github.com/brilliapps/multitypes-multivalues with the newest Mtypes class which is not a perfect solution but wanted to approach the topic completely, example: MTypes<MTypes<int, String>, MTypes<Future, List>> string_or_int_or_whatever - looks ugly, i know!) ). All these solutions to me seem to deliver the same key thing. We can create a function/method with an example param allowing one of the type, f.e. int or String and they are carried f.e. in a .value property of a passed-as-the-param object, and to me it's good, but could be great, if..., and all of this with errors in an editor before compilation.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
request Requests to resolve a particular developer problem state-rejected This will not be worked on
Projects
None yet
Development

No branches or pull requests

2 participants