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

Reply via email to