HaxeFoundation / haxe-evolution

Repository for maintaining proposal for changes to the Haxe programming language
111 stars 58 forks source link

Complex type expressions #44

Closed bendmorris closed 6 years ago

bendmorris commented 6 years ago

Adds a syntax to represent types as expressions: var a = :Array<a.b.MyClass>;

Discussed in https://github.com/HaxeFoundation/haxe/issues/6910, PR: https://github.com/HaxeFoundation/haxe/pull/6913

Rendered version

nadako commented 6 years ago

I'm in favor of this, since I myself had the need to both store type info in metadata and pass it as an argument in a macro function and it always ends up with something ugly like (_:SomeType) with subsequent parsing, so it basically asks to have a syntax for that and :Type syntax makes sense to me too, because of the way we already use colon for type hinting/checking.

ncannasse commented 6 years ago

I don't think the proposal makes sense in Haxe type system. In Haxe MyClass<Int> and MyClass<Float> are both the same value MyClass (which is the class itself). And there's no way to represent MyClass<Int> at runtime so it can't be runtime type checked etc. (for instance we forbid type parameters in "catch" for this reason).

So my first question would be : what problem is this proposal trying to solve?

bendmorris commented 6 years ago

I agree that it is somewhat useless at runtime, but it is rather inconsistent that these expressions do already have a runtime value, but no syntax can express them (as mentioned I can get the runtime "value" via a typedef now.) The main use case for me is to use complex types in metadata and as arguments to macros, in an officially supported way without hacks like (_:MyType<T>). This other proposal has one concrete example case, and I can imagine many others:

extern class Array<T> {
    @:const(:Array<Const<T>>) function concat(a:Const<Array<T>>):Array<T>;
}

(Obligatory "yo dawg, I heard you like evolution proposals...")

In Haxe MyClass<Int> and MyClass<Float> are both the same value MyClass

This isn't true of @:generic classes at least. How would I reference a specifically parameterized generic class and pass it to Std.is?

@:generic class MyClass<T> {
    var val:T;

    public function new(val:T) this.val = val;
}

class Test {
    static function main() {
        var e = new MyClass<Int>(1);
        trace(Std.is(e, MyClass));          // false
        trace(Std.is(e, MyClass<Int>));     // doesn't compile
    }
}
bendmorris commented 6 years ago

And note that again this is possible once I add a typedef, because the only barrier is syntax:

typedef MyClassInt = MyClass<Int>;

trace(Std.is(e, MyClassInt));       // true

So currently we need superfluous typedefs in order to reference a specific @:generic class at runtime.

ncannasse commented 6 years ago

I don't think that it justify adding new syntax that is very specific to this case.

In both cases, adding new syntax should only be done when it clearly opens new possibilities that wouldn't be possible otherwise. The more syntax a language has the more complex it is to learn/read. Also, using : for this case locks it for other future (and better) uses, so I think this proposal should be rejected.

ncannasse commented 6 years ago

I agree, but I don't think that justify adding a specific syntax just for that.

On Wed, Apr 18, 2018 at 9:04 AM, Ben Morris notifications@github.com wrote:

And note that again this is possible once I add a typedef, because the only barrier is syntax:

typedef MyClassInt = MyClass; trace(Std.is(e, MyClassInt)); // true

So currently we need superfluous typedefs in order to reference a specific @:generic class at runtime.

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/HaxeFoundation/haxe-evolution/pull/44#issuecomment-382284768, or mute the thread https://github.com/notifications/unsubscribe-auth/AA-bwMNMYyXrhNsuzXPKXeVaf8I6AgZsks5tpuV6gaJpZM4SvUSF .

bendmorris commented 6 years ago

Fair enough (although short lambdas are an obvious counterexample which opened no new possibilities.) If we did adopt something that made use of this (such as the Const<T> proposal) I think opinions might shift on whether this workaround is acceptable. i.e. I don't think officially requiring:

    @:const(var _:Array<Const<T>>) function concat(a:Const<Array<T>>):Array<T>;

in user code and the std library would be reasonable.

Simn commented 6 years ago

I'm also not really sold on this one. I acknowledge that it makes a specific case a bit nicer, but that alone is not enough to justify adding a new syntax.

back2dos commented 6 years ago

Hmm, maybe @FrancisBourre can comment on whether or not this is useful.

We currently use strings and parse those at compile time, e.g. @type('String -> Int -> Bool'). We have that a few hundred times in our code base. And we have @Dependency(var _:SomeType) another few hundred times.

ncannasse commented 6 years ago

I'm voting for a close until someone propose a better solution.