-
-
Notifications
You must be signed in to change notification settings - Fork 658
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
Abstract unification problem regarding functions #10336
Comments
I guess that when the compiler tries to unify with the |
For abstracts over functions top down inference always tries the underlying type. You can remove the class Test {
static function main() {
var value = 'foo';
var f:Func<String> = function (v:String) return value = v; // String should be Unit
}
}
abstract Func<T>(T->Unit) {
@:from
public static function fromFuncReturningSameType<T>(f:T->T):Func<T> {
throw 'irrelevant';
}
}
class Unit {} This is somewhat related to #3525. |
This actually fixes both cases because top-down inference is only applied if there is exactly one This might cause some regressions if somebody relies on the inference against the underlying type situation, but I really consider this a bugfix either way. |
Well... turns out stuff is failing. If I'm seeing this correctly, this comes from our abstract PromiseHandler<T, TOut>(T->Dynamic) // T->Dynamic, so the compiler always knows the type of the argument and can infer it for then/catch callbacks
from T->Promise<TOut>
from T->Thenable<TOut>
from T->TOut
{} |
Perhaps can we we have some sort of documentation to describe how these are expected to work? For example the following used to work but not in nightly abstract Callback<T>(T->Void) from (T->Void) {
// in nightly one has to comment out this @:from to make it compile
@:from static function fromMany<A>(callbacks:Array<Callback<A>>):Callback<A>
throw 0;
}
function main() {
var cb:Callback<Foo> = foo -> trace(foo.value);
}
abstract Foo(Dynamic) {
public var value(get, never):Int;
inline function get_value() return 1;
} |
What? Why? I mean if there's a |
As far as the part of inferring against the underlying type if there's no As for the rest, it's now really borked. I've even tried with abstract Callback<T>(T->Void) from (T->Void) {
@:from static function ofSingle<A>(cb:A->Void):Callback<A>
return null;
@:from static function fromMany<A>(callbacks:Array<Callback<A>>):Callback<A>
throw 0;
}
function main() {
var cb:Callback<String> = foo -> trace(foo.length);// compiles, but presumably because `{ length: Unknown<?> }` unifies with `String`
var cb:Callback<Foo> = foo -> trace(foo.value);// fails, because (foo : { value : Unknown<0> }) -> Void should be Callback<Foo>
}
abstract Foo(Dynamic) {
public var value(get, never):Int;
inline function get_value() return 1;
} This breaks tink_core irreparably. Quite possibly other things, but I didn't get to check, because nothing compiles anymore :D |
Well, this is how our top-down inference with regards to abstracts has always worked. But I agree that we're now having problems. The thing with top-down inference is that we don't know about the "presence of a compatible from" until after typing, and then it might be too late. That's why we only apply it if there's exactly one But clearly we have to be more clever here. |
Hmm, not really. As far as I can tell, it relied on the underlying type. Which in general violates the abstraction. But not when there's a What I don't get is how the current solution is even supposed to work. If there's only a Now, I think the simplest solution is to use any
Yes and no. Years ago I suggested syntax should be taken into account, which you were opposed to for unmemorable reasons. Here's my thinking: If the expected type is an abstract and the expression is an Selecting by syntax could also be applicable for |
See if that helped. I had actually implemented what you're describing, but it was cancelling everything upon encountering a non-function type. Now that I think about it, that doesn't make any sense. |
Hmm. The debate got spread out a bit. I'll try to bring it all together here, at the risk of being repetitive, lengthy and stating the obvious. So what's currently wrong (up to 4.2.5)? In the words of Simn:
There's no arguing about that. If the author of an abstract decides to change the underlying type, no code should be affected. However, there's something quite strongly related to the underlying type, which are any class Test {
static function main() {
var i1:Iterable<String->Void> = [];
var i2:Iterable<TransparentFoo> = i1;// just fine
var i3:Iterable<OpaqueFoo> = i1;// String -> Void should be OpaqueFoo
}
}
abstract TransparentFoo(String->Void) from String->Void {}
abstract OpaqueFoo(String->Void) {
@:from static function ofFunction(f:String->Void)
return (cast f:OpaqueFoo);
} Even though most of the time the underlying type and the The type system assigns a very strong meaning to I guess there are two questions:
To circle back to the issue that sparked this, I would say that it should infer against |
Thank you for the summary, this is quite helpful! Just a quick note because I don't have time right now: One particular problem with your idea is that small "applicable from" part. We don't necessarily know if there's an applicable from until we know the type of the expression. There could be cases where using the |
Well, I'm really thinking pure syntax here. In the case of functions perhaps even arity, as detailed in a previous comment.
But until now such behavior was not supported in an official release. If you have a |
We're already dealing with that by considering arity. The remaining cases are the ones where the arity is equal. See #10604.
That's not true, before e134a97 the compiler would simply always use the underlying type and not even look at any |
Hmm, there seems to be a misunderstanding. I think you're saying what I'm saying, but obviously you seem to think we're saying different things :D Let's reiterate the problem you've raised:
There are two ways to read that statement:
I've chosen the second interpretation of the statement, because breaking existing code is more concerning. And in that context I have pointed out that we've been inferring against the underlying type and so if there's a As for the question whether in the future we may wish to make the above example compile - which is what this issue was originally about - I'm leaning towards not supporting it. But I'm not sure that's what we're discussing ^^ |
See #10604 |
Link: #8476
The text was updated successfully, but these errors were encountered: