On Mon, Feb 3, 2014 at 11:20 PM, Eric Reed <[email protected]> wrote:
> 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.
>
'static is not even a trait per se (as far as I understand it), it merely
states the lifetime which data must be valid for. I would not expect this
to imply "oh, and you can also try casting it to any type". I'm not sure
what a precise definition of parametricity is that we could apply here, but
I'd be very surprised if this flies. It should mean something like "only
information that is provided may be used", not "if no information is
provided, nothing may be assumed, but if even a little information is
provided, well feel free to do whatever you like".
>
> 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 <[email protected]> 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 <[email protected]>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 <[email protected]>wrote:
>>>
>>>> On Sat, Feb 1, 2014 at 6:24 PM, Eric Reed <[email protected]>
>>>> 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
>>> [email protected]
>>> https://mail.mozilla.org/listinfo/rust-dev
>>>
>>>
>>
>
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev