-
Notifications
You must be signed in to change notification settings - Fork 109
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
“ Failed to compile expression due to unresolved type constraints” for unused lambda in let clause #279
Comments
Hi @baiyanhuang, IIRC, the variable Perhaps it's a good idea to provide user some friendly error messages in case of such errors. |
Exactly, we can't produce output code until we're certain that the expression that we're compiling has a fixed type (a monotype). Because that function in your let expression is polymorphic, even though you don't use it, the whole expression is polymorphic. In the general case, you could have an expression like "let x=E in ()" or "let x=E in P" where x is not used in P. If we didn't have to worry about side-effects, then we could just rewrite those expressions to "()" or "P" and then they'd have monotypes and it would be fine. But we do have to worry about side-effects, since "E" could have some other effect. And because E is polymorphic, it might have a different effect depending on the indexed type (e.g. for ints, launch missiles, for doubles, buy a boat, etc). In your specific case, because the expression is a function definition (but not because it has a function type, since expressions with side-effects can have function types, like e.g. "let _=print(42) in (+)"), it would be possible to drop the unused function definition without changing the meaning of the program. So TLDR, it might be worthwhile to allow this and quietly drop the function definitions in let expressions where they aren't used. |
Thanks Kalani, Kai, so in
what is the rationale behind that? |
No it's possible to have polymorphic definitions in let expressions too. But global polymorphic functions are also very useful, since you might use 'foo' one way (with an std::string argument) and then also another way (with an array argument). It is very common for us to do this kind of thing. We like polymorphic definitions. But where we started was that you had an expression, which happened to be a let expression, and you used it in a context where it needed to have a definite monotype (e.g. it had to be a function taking in an array and returning bool). So the problem was that you had a point where you said that your expression had to have this definite monotype, but then type analysis determined that it didn't have that type (and in your case, the reason was that it had this polymorphic inner definition that wasn't decided). That's where the problem came from -- in your program you have something like 'c.compileFn<bool(const array*)>(exp)' and so there's no choice but to require that 'exp' has that definite monotype. Does that make sense? It's not the case that types always have to be definite monotypes in let expressions. For example, you can have a polymorphic function like:
That function is (bounded) polymorphic, since you can use it with int, double, etc (any type supporting addition and multiplication). Further, its inner let expression is polymorphic. That's all fine, because it's not defined in a context where it is required to have a definite monotype. Hope this helps. |
Thanks Kalani, I got one more question:
Why is this? and what is difference between inside let, or in global? when I have the same function defintion? |
Well what I meant there is that you might force the monotype requirement by how you use the If you have a let expression, the whole expression type depends on the definitions you include and those definitions are only scoped for that expression. So you could write something like:
There you have two let expressions with two independent scopes, even though they use the same variable name the bindings are independent. You can make definitions that have polymorphic let bindings, though in general it's important to have definitions with constraints (or unbound type variables) that are coherent (have some eventual path to monotype resolution). This is what an incoherent definition looks like:
There you have a polymorphic let, but you'll never be able to use it for anything because there's nothing that relates its values to its type variables. It might be worth rejecting definitions like this too. |
Above code will produce compile error:
But below cases are all ok:
What might be the explanation for such behavior?
The text was updated successfully, but these errors were encountered: