I'm not a huge fan of this proposal. It makes declarations longer, and it
removes the visual consistency of Foo<T,U> everywhere, which I think
introduces its own pedagogical issue.
The recent addition of default type parameters, though, makes me think
there's a reasonable change that increases consistency and shortens
declarations in a few common cases.
>From what I understand, the reason we can't just have
impl Trait<T> for Foo<T,U>
is because it's ambiguous whether T and U are intended to be concrete or
generic type names; i.e.,
impl<T> Trait<T> for Foo<T,U>
tells the compiler that we expect U to be a concrete type name.
Our new default type parameter declarations look like:
struct Foo<T,U=Bar>
So what if to actually make generic types concrete, we always used the '='?
struct Foo<T,U=Bar>
impl Trait<T> for Foo<T, U=Derp>
This saves a character over 'impl<T> Trait<T> for Foo<T, Derp>', solves the
greppability problem, and makes intuitive sense given how defaults are
declared.
It also has a nice parallel with how ':' is used - ':' adds restrictions,
'=' fully locks in place. So what is today something like
impl<T:Ord> Trait<T> for Foo<T, Derp>
would become
impl Trait<T:Ord> for Foo<T, U=Derp>
The rule would be that the first use of a type variable T would introduce
its bounds, so for instance:
impl Trait<T:Ord> for Foo<Z:Clone, U=Derp>
would be fine, and
impl Trait<T> for Foo<T:Clone, U=Derp>
would be an error.
More nice fallout:
struct Foo<A,B>
impl Foo<A,B=Bar> {
fn one(a: A) -> B
fn two(a: A) -> B
fn three(a: A) -> B
}
means that if I ever want to go back and change the name of Bar, I only
have to do it in one place, or if Bar is actually some complicated type, I
only had to write it once, like a little local typedef.
I'm sure this has some glaring obvious flaw I'm not thinking of. It would
be nice to have less syntax for these declarations, but honestly I'm ok
with how it is now.
On Sat, Feb 1, 2014 at 5:39 PM, Corey Richardson <[email protected]> wrote:
> Hey all,
>
> bjz and I have worked out a nice proposal[0] for a slight syntax
> change, reproduced here. It is a breaking change to the syntax, but it
> is one that I think brings many benefits.
>
> Summary
> =======
>
> Change the following syntax:
>
> ```
> struct Foo<T, U> { ... }
> impl<T, U> Trait<T> for Foo<T, U> { ... }
> fn foo<T, U>(...) { ... }
> ```
>
> to:
>
> ```
> forall<T, U> struct Foo { ... }
> forall<T, U> impl Trait<T> for Foo<T, U> { ... }
> forall<T, U> fn foo(...) { ... }
> ```
>
> The Problem
> ===========
>
> The immediate, and most pragmatic, problem is that in today's Rust one
> cannot
> easily search for implementations of a trait. Why? `grep 'impl Clone'` is
> itself not sufficient, since many types have parametric polymorphism. Now I
> need to come up with some sort of regex that can handle this. An easy
> first-attempt is `grep 'impl(<.*?>)? Clone'` but that is quite
> inconvenient to
> type and remember. (Here I ignore the issue of tooling, as I do not find
> the
> argument of "But a tool can do it!" valid in language design.)
>
> A deeper, more pedagogical problem, is the mismatch between how `struct
> Foo<...> { ... }` is read and how it is actually treated. The
> straightforward,
> left-to-right reading says "There is a struct Foo which, given the types
> ...
> has the members ...". This might lead one to believe that `Foo` is a single
> type, but it is not. `Foo<int>` (that is, type `Foo` instantiated with type
> `int`) is not the same type as `Foo<unit>` (that is, type `Foo`
> instantiated
> with type `uint`). Of course, with a small amount of experience or a very
> simple explanation, that becomes obvious.
>
> Something less obvious is the treatment of functions. What does `fn
> foo<...>(...) { ... }` say? "There is a function foo which, given types ...
> and arguments ..., does the following computation: ..." is not very
> adequate.
> It leads one to believe there is a *single* function `foo`, whereas there
> is
> actually a single `foo` for every substitution of type parameters! This
> also
> holds for implementations (both of traits and of inherent methods).
>
> Another minor problem is that nicely formatting long lists of type
> parameters
> or type parameters with many bounds is difficult.
>
> Proposed Solution
> =================
>
> Introduce a new keyword, `forall`. This choice of keyword reads very well
> and
> will not conflict with any identifiers in code which follows the [style
> guide](https://github.com/mozilla/rust/wiki/Note-style-guide).
>
> Change the following declarations from
>
> ```
> struct Foo<T, U> { ... }
> impl<T, U> Trait<T> for Foo<T, U> { ... }
> fn foo<T, U>(...) { ... }
> ```
>
> to:
>
> ```
> forall<T, U> struct Foo { ... }
> forall<T, U> impl Trait<T> for Foo<T, U> { ... }
> forall<T, U> fn foo(...) { ... }
> ```
>
> These read very well. "for all types T and U, there is a struct Foo ...",
> "for
> all types T and U, there is a function foo ...", etc. These reflect that
> there
> are in fact multiple functions `foo` and structs `Foo` and implementations
> of
> `Trait`, due to monomorphization.
>
>
> [0]:
> http://cmr.github.io/blog/2014/02/01/polymorphic-declaration-syntax-in-rust/
> _______________________________________________
> Rust-dev mailing list
> [email protected]
> https://mail.mozilla.org/listinfo/rust-dev
>
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev