On 04/28/2013 10:38 AM, Patrick Walton wrote:
Hi everyone,
The reactions to this bug on impls [1] have caused me to think that
the current treatment of paths in generic type and trait
implementations is something of a wart and perhaps should be reworked.
Specifically, the problem is that this:
impl<T> MyType<T> {
fn new<U>() -> MyType<T> { ... }
}
Cannot be accessed (as you might expect) like so:
MyType::<int>::new::<float>()
But instead you must concatenate the type parameters like this:
MyType::new::<int,float>()
This is highly unintuitive.
Basically, all of this is an artifact of the fact that, internally, we
treat a type implementation as essentially a module, and modules don't
have type parameters. This fact also brings with it an unfortunate
issue relating to typedefs, namely that you cannot call static methods
through them. You might wish to write:
type IntMyType = MyType::<int>;
MyIntType::new::<float>()
But you can't. In fact, you can't call static methods *at all* through
typedefs, meaning that this doesn't work:
impl MyOtherType {
fn new() -> MyOtherType { ... }
}
type Alias = MyOtherType;
Alias::new() // doesn't work
This severely reduces the utility of typedefs.
I've been thinking about ways we could fix this without severely
complicating the compiler, and I think I've got the sketch of an idea
that might work. I propose that we change type implementations (and
traits) to be less module-like in the following ways:
1. Forbid `use` statements from importing from type implementations or
traits. Having `use` statements take type parameters would severely
complicate an already complex resolution pass, and might even require
the name resolution and typechecking phases to be intertwined in an
incoherent way.
2. Add a new type of `path` to the grammar:
'::'? identifier ('::' identifier)* '::' '<' type_parameters '>'
'::' identifier ('::' identifier)* ('::' '<' type_parameters '>')?
This production admits paths like `MyType::<int>::new::<float>()`.
3. Internally, whenever the resolve pass sees one of these paths, or
sees a component in a pass resolve to a typedef, it drops the type
parameters and follows any typedefs to find the associated `impl` or
trait.
4. When the typechecker sees such a path, it resolves the type portion
of that path. If the type didn't resolve to a nominal monotype, it
reports an error. Otherwise, it pulls the type parameters out of that
nominal type and concatenates them with the type parameters supplied
in the rest of the path, if any, to produce the "real" set of type
parameters used by typechecking and translation.
These semantics should allow all type parameters to be dropped if they
can be inferred, as today, but should allow typedefs to "just work".
Unless I'm missing something. :)
This is not a backwards compatible change, but I don't expect much
code to be impacted: I suspect `use` from a trait or type
implementation is rare, and concatenated type parameters even more so.
Thoughts?
bjz's use cases are pretty compelling, but restricting `use` like that
is very unfortunate, making 'uniform method syntax' not quite uniform.
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev