Skip to content

Conversation

@glaebhoerl
Copy link
Contributor

@gsingh93
Copy link

I've heard that we can't just have foo<T> because of a parsing ambiguity. However, other languages do it fine. The use of a symbol (regardless of which symbol, @ or ::) between a method name and the template type is one thing I really dislike in Rust.

@ghost
Copy link

ghost commented Jan 11, 2015

Not quite sure what I think of the @ syntax (I don't dislike it) but +1 for the sentiment. I think something other than :: would be nice.

@Gankra
Copy link
Contributor

Gankra commented Jan 11, 2015

Is it not simply too late to make this change? (I'm happy with ::, though would be happier with not needing anything)

@nrc
Copy link
Member

nrc commented Jan 11, 2015

-1, too late and inconsistent - as much as I dislike :: in paths like this, it is at least something that is there already, @ is just randomness.

@glaebhoerl
Copy link
Contributor Author

Too late, yes, but I don't think the second half makes sense - it's not a path, and so shouldn't use path syntax. That's the entire point.

@ftxqxd
Copy link
Contributor

ftxqxd commented Jan 12, 2015

Another drawback: this would probably prevent the addition of a @ operator, as foo@<T>::bar could be parsed as either (foo) @ (<T>::bar) (using UFCS syntax) or as the equivalent of today’s foo::<T>::bar.

FWIW, I prefer the current syntax anyway—I heavily dislike the @ character because of its noisiness. I’ve considered the idea of using for<> everywhere for generics, so generic functions would be declared as for<'a> fn foo(), have type for<'a> fn(), and be valled as for<'static> foo(). The main problem with this approach (apart from verbosity) is what to do with methods: iter.for<Vec<int>> collect() looks a little strange. Also, it might conflict with UFCS if we support it in patterns—deciding whether to parse for <T> as a for loop or a generic function would (I think) require infinite lookahead.

@glaebhoerl
Copy link
Contributor Author

Oh, there's that dreadful UFCS syntax. I forgot about that. Reflecting the type-level for syntax down is a cute idea. (You could just require methods be called with UFCS syntax to allow specifying types for them?)

@nikomatsakis
Copy link
Contributor

What nrc said, I think. I'd be happy if we could have removed the :: altogether, but it didn't happen, and I think it's ok. (At least, all the proposals for removing it that I saw involved potentially quite far-reaching changes, such as adjusting the tokenizer, and I think that those are out of scope now.) Anyway, what I really dislike about the current situation is that (a) editors/macros can't trivially tell whether < is a grouping character and (b) paths in types and paths in expressions have different syntax. Neither shortcoming seems to be addressed by this proposal.

@nstoddard
Copy link

Strong +1. The :: syntax is easily the worst syntax in Rust right now, and it really confused me when I had to use it for the first time. The @ syntax may not be perfect, but I'd vastly prefer it over ::.

As far as breaking changes go, this one doesn't look like it should be much of a problem. It only requires an easy find+replace. Rust is still in alpha anyway so people are expecting the occasional breaking change.

@eddyb
Copy link
Member

eddyb commented Jan 14, 2015

In this reddit thread, I thought of foo!<T> and foo.<T>, but @bstrie later told me that the latter used to be the syntax before foo::<T> came along.

Today I came up with another version of this proposal, foo-<T>.
I prefer it because of the "connection" between the two tokens, which is not really the case for any other proposal I've seen.

// for comparison - past, current, and 3 proposals:
"42".parse.<int>()
"42".parse::<int>()
"42".parse@<int>()
"42".parse!<int>()
"42".parse-<int>()

While the lines aren't actually touching, Hasklig can make it so, which I expect to be quite pleasing to the eye, in the end (we should fork it to remove the <<, >>, <<< and >>>, everything else works nicely for Rust).

On the other hand: @nikomatsakis has been mentioning type ascription syntax quite a lot lately, and I do like that prospect. Everyone I've seen using such syntax (in a hypothetical/pseudocode context) goes for x: T and I agree that fits in very nicely with the rest of the language. But given UFCS, this can lead to some confusing situations:

// The contrived setup:
struct Foo<T>;
impl<T> Foo<T> {
    fn Baz() -> Foo<T> {Foo} // could also be an associated constant
}
struct Bar;
impl Bar {
    type Baz = Foo<Bar>;
}
// The following two expressions are both valid but different:
Foo::<Bar>::Baz // type fn() -> Foo<Bar>
Foo:<Bar>::Baz // type Foo<Bar>

Whether or not this will be an actual issue, I can't tell, but it seems like a good argument in favor of changing :: in ::<T>.

@sinistersnare
Copy link

If people really want to change it, I can see that and if we find something nice looking, I could be for it.

Here is a few ways this could be written with keys on my keyboard 👅:

  • foo@<T>()
  • foo-<T>()
  • foo^<T>()
  • foo#<T>()
  • foo$<T>()
  • foo%<T>()

@ghost
Copy link

ghost commented Jan 14, 2015

Strong +1. The :: syntax is easily the worst syntax in Rust right now, and it really confused me when I had to use it for the first time.

It's confusing even after using it for some time. I'd say a good percentage of the compile errors I get are because I messed this up and they sometimes take awhile to figure out too…

@netvl
Copy link

netvl commented Jan 15, 2015

-1. There's nothing wrong with ::<T>. @ is much more heavy and just hurts readability for no gain.

@eddyb
Copy link
Member

eddyb commented Jan 15, 2015

@NervL see my description above of an UFCS/TA complication. But I do agree @ could be considered worse than :: (and that's ignoring its unfortunate past usage or the attr/macro proposal).

@sinistersnare
Copy link

I think foo^<T>() is actually nice. Clojure uses it as its type hint syntax (the ^, not the whole expression), and I think it is ok.

@ftxqxd
Copy link
Contributor

ftxqxd commented Jan 15, 2015

foo^<T>(), foo%<T>(), and foo-<T>() are ambiguous due to UFCS, as I remarked about @ above.

A note about the RFC: I believe the syntax as the RFC currently proposes it is ambiguous with UFCS in contrived cases. This is because @ is a binary operator in patterns, and all paths are valid patterns. <Foo>::Bar is a valid path (and thus pattern) with UFCS. Therefore, foo@<Bar>::baz is a valid pattern today (or would be if we had UFCS), equivalent to foo@(<Bar>::baz). However, this RFC proposes changing foo::<T> to foo@<T>. foo::<Bar>::baz is a valid pattern today; under this RFC, that would be foo@<Bar>::baz. But this conflicts with the earlier interpretation of foo@<Bar>::baz as foo@(<Bar>::baz)! The fix is trivial: either don’t require @-disambiguation in patterns (this is unambiguous because < is not an operator in patterns), or don’t accept the full UFCS syntax in patterns, as I think it’s useless there anyway. (The latter approach is taken by @eddyb’s UFCS-in-expressions patch anyway, AIUI.)

One case where UFCS in patterns might be usable (but not necessarily useful), although it’s probably not worth supporting:

trait Foo {
    type T;
}

enum Bar { Baz, Qux }
impl Foo for () {
    type T = Bar;
}

fn main() {
    match get_a_bar() {
        // Because `<() as Foo>` is a definite, non-generic type, it might be
        // usable as an alias for `Bar`
        <() as Foo>::Baz => ...,
        <() as Foo>::Qux => ...,
    }
}

@eddyb
Copy link
Member

eddyb commented Jan 15, 2015

@P1start I did have a nagging feeling about it but it took me too long to notice. Not even my (somewhat beautiful) foo-<T> escapes the UFCS ambiguity.
Unless we make -< a token, but that's a bit too much to ask.

@theemathas
Copy link

What about using ::<T> for all declarations and usages?

fn foo::<T: Trait>(x: T) {...}
impl::<T> Trait for Foo::<T> {...}

It looks a bit weird but it's much more consistent.

@viperscape
Copy link

foo^(), foo%(), and foo-() are ambiguous due to UFCS

@P1start dang, I kind of liked ^, it does work well for hints in Clojure like @sinistersnare states (I thought I remembered you from #clojure)

I do not like the @ so much, if it does get changed it should be something terrific to warrant it.

I suppose foo~T is out..?

@Ericson2314
Copy link
Contributor

At this point, I would get rid of / change the comparison operators to free up < and >. Generics are far more common than comparisons in my experience. C programmers be damned.

Also, regarding UCFS, I know we have magic self and are not doing things the haskell way, but even so I still think path::Trait<T>::item looks better than path::<T as Trait>::item and removes the need for special syntax.

In sum:

  • shifts: <<<, >>>
  • comps: <<, >>, <=, >=
  • path::Trait<T /* Self */, t-args>::poly_item<t-args>(args)

@nikomatsakis
Copy link
Contributor

Thank you very much for the work you put into this RFC. However, at the triage meeting today we decided to close this issue. While the ::<T> syntax has few fans, this new proposal doesn't seem to be a big improvement -- or, at least, not a big enough improvement to warrant breaking backwards compatibility.

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

Successfully merging this pull request may close these issues.