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

Calling Context.defineModule in Context.onTypeNotFound still errors #4234

Open
back2dos opened this issue May 19, 2015 · 23 comments
Open

Calling Context.defineModule in Context.onTypeNotFound still errors #4234

back2dos opened this issue May 19, 2015 · 23 comments
Milestone

Comments

@back2dos
Copy link
Member

Because TypeDefinition lacks a few features (mostly import and using), it would be nicer to be able to call defineModule and have the compiler check again whether the type is still undefined after invoking all callbacks. I know that"s not exactly pretty, but at least it wouldn"t break any APIs.

@Atry
Copy link
Contributor

Atry commented May 19, 2015

This issue may duplicate with #3262

@back2dos back2dos reopened this May 19, 2015
@back2dos
Copy link
Member Author

Sorry, for the noise. The discussion in the other issue at some point gets very close to this, but it seems I am actually after something slightly different after all.

@Simn
Copy link
Member

Simn commented May 19, 2015

I don"t know why all issues about Context.defineModule confuse me so much that I never quite understand what exactly you guys want. It has arguments for import and using now, what else is missing?

@back2dos
Copy link
Member Author

back2dos commented May 19, 2015

Hope this clarifies: there is no good way to combine the defineModule API (which does everything I want it to) and onTypeNotFound.

I would like this to work:

package;

class Test {

    #if macro
    static function use() { 
        haxe.macro.Context.onTypeNotFound(function (name:String) {
            if (name == "Foo") {
                var type = macro class Foo { };
                haxe.macro.Context.defineModule("Foo", [type]);
                //return type;
            }
            return null;
        });
    }
    #else
    static function main() {
        Foo;//yields "Unknown identifier : Foo
" or when the above return is uncommented, yields "Type name Foo is redefined from module Foo"
    }
    #end
}

As the code above shows, the module does get defined, but returning null causes the compiler to think that it wasn"t.

Short of adding an onModuleNotFound, that allows to returning all the data for a module, I think checking again would do the trick.

@Simn
Copy link
Member

Simn commented May 19, 2015

But what"s wrong with return type there?

@back2dos
Copy link
Member Author

As I said, that"s interpreted as a duplicate definition. I already define the type with defineModule (in the actual code I need using and private classes and what not). If I then return type again, the compiler rejects it.

@Simn
Copy link
Member

Simn commented May 19, 2015

Ah, now I get it.

@Simn
Copy link
Member

Simn commented Jun 12, 2015

I wish we could change the callback type of onTypeNotFound to String -> Null<ComplexType> instead of String -> TypeDefinition...

@ousado
Copy link
Contributor

ousado commented Jun 16, 2015

Hm, how about returning a typedef to the type in the module? Or could we add a special typedef or abstract that"s parameterized with the desired type within the module? It doesn"t feel right to have the compiler do the lookup a second time for every null from onTypeNotFound.

@back2dos
Copy link
Member Author

back2dos commented Jun 17, 2015

@ousado I was hoping for the typedef to work, but that yields a duplicate type error. But it would do the trick. The special type doesn"t feel right either ;)

@Simn Why not Type altogether? And then just do something like:

abstract TypeNotFoundCallback(String->Null<Type>) from String->Null<Type> {
  @:from static function fromLegacyVersion(f:String->Null<TypeDefinition>):TypeNotFoundCallback
    return function (name) return switch f(name) {
      case null: null;
      case v:
        //the compiler currently just ignores the name and some code may rely on that
        var parts = name.split(".");
        v.name = parts.pop();
        v.pack = parts;
        Context.defineType(v);
        Context.getType(name);
    }
}

It might bite a few people who depended on inference, but I"d say that"s fair enough. Happens to me all the time :D

@Simn
Copy link
Member

Simn commented Jun 17, 2015

There were reasons for changing @:genericBuild macros to return ComplexType instead of Type. I don"t remember these reasons right now but my guess is they would apply here as well.

@back2dos
Copy link
Member Author

Alright, forget that bit. What about building it over String->Null<ComplexType> analogously?

@Simn
Copy link
Member

Simn commented Jun 17, 2015

I"m curious, does @:deprecated work on fromLegacyVersion?

@back2dos
Copy link
Member Author

back2dos commented Dec 2, 2015

Are there any news on this? I don"t want to be impatient, but if this is easy to add, then I"d appreciate it a lot. I have found my own workarounds, but they make any reported errors less than useless.

@Simn
Copy link
Member

Simn commented Dec 2, 2015

Well, the last comment was me asking something so I guess I was waiting for a reply.

I still think changing the return type to Null<ComplexType> is the way to go, I would just like to avoid compatibility problems as much as possible.

@back2dos
Copy link
Member Author

back2dos commented Dec 2, 2015

Well, there"s 10 occurrences of Context.onTypeNotFound on GitHub, so if there ever was a good point in time to afford compatibility problems, I would think this is it.

But if you do feel differently, why not have the compiler side expect one and make the API accept both?

static function onTypeNotFound(cb:EitherType< String->Null<ComplexType> , String->Null<TypeDefinition>>) {
  function find(name:String):Null<ComplexType> {
    var ret:Dynamic = cb(name);
    return
      if (ret == null || Std.is(ret, ComplexType)) ret;
      else {
          var td:TypeDefinition = ret;
          Context.defineType(td);
          TPath( /* ... the path either from the td or from the name ?... */ );
      }
  } 
  load("on_type_not_found",1)(find);
}

@Atry
Copy link
Contributor

Atry commented Feb 10, 2016

I propose adding a new function:

public static function onModuleNotFound(cb:String->Array<TypeDefinition>);

So we can return all types in that module.

@Simn Simn modified the milestone: 3.4 Feb 23, 2016
@Simn Simn modified the milestones: 3.4, 4.0 Jan 9, 2017
@Simn
Copy link
Member

Simn commented Sep 28, 2017

I"ve looked at this for a bit to understand the problem. The onTypeNotFound functionality hooks into our internal load_extern_type feature. This was originally used for -swf-lib and later extended to -net-lib and -java-lib. Due to that origin, it really does expect an actual type definition to be returned.

What you want here is a bit different: It"s essentially a path forwarding/rewriting, with the option of using the input path again after defining a type. This means we have to handle it differently internally. This isn"t hard to do, the only question is if onTypeNotFound in its current form should stick around. And I"m not really seeing a good reason for that, so I"m leaning towards just breaking it now (by replacing TypeDefinition with ComplexType).

This would allow infinite recursion if you return the input path without actually defining the type. That may or may not be the user"s fault though...

@ousado
Copy link
Contributor

ousado commented Sep 29, 2017

How about returning Either<TypeDefinition,ComplexType>? It"s still a breaking change, but making existing code work is as trivial as it gets, and if the underlying machinery for TypeDefinition sticks around anyway, it"d be easy to support both, right?

@back2dos
Copy link
Member Author

ComplexType seems just fine to me. If you have a TypeDefinition then you need to define it and return a type path to it. It"s not "as trivial as it gets", but close enough to me at least.

If we really want to avoid breaking things, I would propose to use EitherType instead, because one case is an enum and the other an object, so it"s easy to check.

@Simn
Copy link
Member

Simn commented Sep 30, 2017

Slightly related: Context.defineType could return a ComplexType which identifies the defined type.

@ousado
Copy link
Contributor

ousado commented Sep 30, 2017

EitherType is a hack to deal with hacks. It shouldn"t be used in any API one has control over, let alone in a core compiler API.

@Simn
Copy link
Member

Simn commented Aug 16, 2019

Coming back to this, I realize that using ComplexType doesn"t make sense here. Context.onTypeNotFound is about types, not type instances which is what ComplexType represents. Even TypePath isn"t correct because it includes type parameters, which is again about type instances.

I think what we want here is a simple dot path, aka a String.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants