On Sun, Feb 2, 2014 at 6:08 PM, Benjamin Striegel <[email protected]>wrote:
> After sleeping on it I'm not convinced that this would be a net > improvement over our current situation. With a few caveats I'm really > rather happy with the syntax as it is. > > > On Sun, Feb 2, 2014 at 8:55 AM, Jason Fager <[email protected]> wrote: > >> 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(...) { ... } >>> ``` >>> >>> >From a readability point of view, I am afraid this might be awkward though. Coming from a C++, I have welcome the switch from `typedef` to `using` (aliases) because of alignment issues; consider: typedef std::map<int, std::string> MapType; typedef std::vector<std::pair<int, std::string>> VectorType; vs using MapType = std::map<int, std::string>; using VectorType = std::vector<std::pair<int, std::string>>; In the latter, the entities being declared are at a constant offset from the left-hand margin; and close too; whereas in the former, the eyes are strained as they keep looking for what is declared. And now, let's look at your proposal: fn foo(a: int, b: int) -> int { } fn foo<T, U>(a: T, b: U) -> T { } forall<T, U> fn foo(a: T, b: U) -> T { } See how "forall" causes a "bump" that forces you to start looking where that name is ? It was so smooth until then ! So, it might be a net win in terms of grep-ability, but to be honest it seems LESS readable to me. -- Matthieu > 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 >> >> > > _______________________________________________ > 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
