Actually this isn't the case. fn foo<T: Any>(t: T) -> TypeId { t.get_type_id() }
compiles just fine, but fn bar<T>(t: T) -> TypeId { t.get_type_id() } fails with "error: instantiating a type parameter with incompatible type `T`, which does not fulfill `'static`". Just <T> does not imply <T: 'static>, so parametricity is not violated. I had the same thought about making size_of and friends unsafe functions. I think that might be a reasonable idea. On Mon, Feb 3, 2014 at 5:35 AM, Gábor Lehel <glaebho...@gmail.com> wrote: > Just because Any is a trait doesn't mean it doesn't break parametricity. > Look at this: > > > http://static.rust-lang.org/doc/master/src/std/home/rustbuild/src/rust-buildbot/slave/doc/build/src/libstd/any.rs.html#37-63 > > Because we have `impl<T: 'static> Any for T`, it can be used with *any > type* (except borrowed data), including type parameters, whether or not > they declare the `T: Any` bound explicitly (which is essentially redundant > in this situation). > > The proper thing would be for the compiler to generate an `impl Any for > MyType` for each individual type separately, rather than a single generic > impl which is valid for all types. > > I also think we should guarantee parametricity for safe code and make > `size_of` an unsafe fn. Its legitimate uses in unsafe code (e.g. smart > pointers) are well encapsulated and don't expose parametricity violations, > and I don't believe safe code has a legitimate reason to use it (does it?). > > > On Sun, Feb 2, 2014 at 3:27 AM, Eric Reed <ecr...@cs.washington.edu>wrote: > >> I'm going to respond to Any and size_of separately because there's a >> significant difference IMO. >> >> It's true that Any and trait bounds on type parameters in general can let >> function behavior depend on the passed type, but only in the specific >> behavior defined by the trait. Everything that's not a trait function is >> still independent of the passed type (contrast this with a setup where this >> wasn't true. `fn foo<A>() -> int' could return 2i for int and spin up a >> tetris game then crash for uint). Any just happens to be powerful enough to >> allow complete variance, which is expected since it's just dynamic typing, >> but there's an important distinction still: behavior variance because of >> Any *is* part of the function because you need to do explicit type tests. >> >> I wasn't aware of mem::size_of before, but I'm rather annoyed to find out >> we've started adding bare A -> B functions since it breaks parametricity. >> I'd much rather put size_of in a trait, at which point it's just a weaker >> version of Any. >> Being able to tell how a function's behavior might vary just from the >> type signature is a very nice property, and I'd like Rust to keep it. >> >> Now, onto monomorphization. >> I agree that distinguishing static and dynamic dispatch is important for >> performance characterization, but static dispatch != monomorphization (or >> if it currently does, then it probably shouldn't) because not all >> statically dispatched code needs to be monomorphizied. Consider a function >> like this: >> >> fn foo<A, B>(ox: Option<~A>, f: |~A| -> ~B) -> Option<~B> { >> match ox { >> Some(x) => Some(f(x)), >> None => None, >> } >> } >> >> It's quite generic, but AFAIK there's no need to monomorphize it for >> static dispatch. It uses a constant amount of stack space (not counting >> what `f' uses when called) and could run the exact same code for any types >> A or B (check discriminant, potentially call a function pointer, and >> return). I would guess most cases require monomorphization, but I consider >> universal monomorphization a way of implementing static dispatch (as >> opposed to partial monomorphization). >> I agree that understanding monomorphization is important for >> understanding the performance characteristics of code generated by *rustc*, >> but rustc != Rust. >> Unless universal monomorphization for static dispatch makes its way into >> the Rust language spec, I'm going to consider it an implementation detail >> for rustc. >> >> >> >> On Sat, Feb 1, 2014 at 3:31 PM, Corey Richardson <co...@octayn.net>wrote: >> >>> On Sat, Feb 1, 2014 at 6:24 PM, Eric Reed <ecr...@cs.washington.edu> >>> wrote: >>> > Responses inlined. >>> > >>> >> >>> >> 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.) >>> > >>> > >>> > I think what I've done in the past was just `grep impl | grep Clone'. >>> > >>> >> >>> >> 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. >>> > >>> > >>> > I strongly disagree with this reasoning. >>> > There IS only one type Foo. It's a type constructor with kind * -> * >>> (where >>> > * means proper type). >>> > Foo<int> and Foo<uint> are two different applications of Foo and are >>> proper >>> > types (i.e. *) because Foo is * -> * and both int and uint are *. >>> > Regarding people confusing Foo, Foo<int> and Foo<uint>, I think the >>> proposed >>> > forall<T> struct Foo {...} syntax is actually more confusing. >>> > With the current syntax, it's never legal to write Foo without type >>> > parameters, but with the proposed syntax it would be. >>> > >>> >>> I've yet to see a proposal for HKT, but with them that interpretation >>> would be valid and indeed make this proposal's argument weaker. >>> >>> >> >>> >> 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). >>> > >>> > >>> > Again, I strongly disagree here. >>> > There IS only one function foo. Some of it's arguments are types. foo's >>> > behavior *does not change* based on the type parameters because of >>> > parametricity. >>> > That the compiler monomporphizes generic functions is just an >>> implementation >>> > detail and doesn't change the semantics of the function. >>> > >>> >>> It can if it uses Any, size_of, etc. eddyb had "integers in the >>> typesystem" by using size_of and [u8, ..N]. Anything using the >>> "properties" of types or the tydescs *will* change for each >>> instantiation. >>> >>> >> >>> >> Another minor problem is that nicely formatting long lists of type >>> >> parameters >>> >> or type parameters with many bounds is difficult. >>> > >>> > >>> > I'm not sure how this proposal would address this problem. All of your >>> > proposed examples are longer than the current syntax equivalents. >>> > >>> >>> The idea is there is an obvious place to insert a newline (after the >>> forall), though bjz would have to comment more on that. >>> >> >> >> _______________________________________________ >> Rust-dev mailing list >> Rust-dev@mozilla.org >> https://mail.mozilla.org/listinfo/rust-dev >> >> >
_______________________________________________ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev