Re: [rust-dev] RFC: Opt-in builtin traits

2014-03-02 Thread Gábor Lehel
On Sat, Mar 1, 2014 at 9:07 PM, Niko Matsakis n...@alum.mit.edu wrote:

 On Fri, Feb 28, 2014 at 11:23:23PM -0800, Kevin Ballard wrote:
  I'm also slightly concerned that #[deriving(Data)] gives the
  impression that there's a trait Data, so maybe that should be
  lowercased as in #[deriving(data)], or even just
  #[deriving(builtin)], but this is a lesser concern and somewhat
  bike-sheddy.

 This is definitely bikeshedding, but that's important too. While we're
 bikeshedding, I think we ought to rename the trait `Pod`, which
 doesn't fit into our verb scheme:

Freeze
Send
Share
Pod


Yes please. It bothers me to no end when an acronym interacts with our
camel case naming convention to form a word that's valid English and has
nothing at all to do with the original meaning. (The other example is of
course `Arc`.) I actually don't really mind if it's `Copy`, `POD`,
`PlainOldData`... just not `Pod`.




 My first thought is to resurrect `Copy`, though of course it's very
 similar to `Clone`. Anyway, let's assume for now we rename `Pod` to `Copy`.

 In that case, I think I would have the following trait sets:

data = Eq, Ord, Clone, Freeze, Hash, Share -- Note: not Copy!
pod  = data, Copy

 The idea is that almost all types (hashtables, etc) can use `data` (in
 particular, `data` is applicable to types with `~` pointers).  Very
 simple types like `Point` can use `pod` (which is, after all, just
 plain old data).


 Niko
 ___
 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


Re: [rust-dev] RFC: Opt-in builtin traits

2014-03-02 Thread Gábor Lehel
On Sat, Mar 1, 2014 at 8:24 PM, Niko Matsakis n...@alum.mit.edu wrote:

 On Fri, Feb 28, 2014 at 10:36:11PM +0100, Gábor Lehel wrote:
  I don't see the difference here. Why do you think this should be handled
  differently?

 To be honest I hadn't thought about it before. I agree there is no
 fundamental difference, though there may be a practical one. I have to
 mull this over.

 I think you're on to something in that there is a connection
 public/private fields, but I'm not quite sure what that ought to
 mean. I dislike the idea that adding a private field changes a whole
 bunch of defaults. (I've at times floating the idea of having a
 `struct`, where things are public-by-default and we auto-derive
 various traits, and a `class`, where things are private-by-default and
 everything is opt in, but nobody ever likes it but me. It's also
 unclear how to treat enums in such a scheme.)


From this angle I can see more of its appeal... in fact I've been having
vaguely similar ideas. This also ties in to the [nontrivial questions][1]
surrounding how to remove `priv` while making things consistent and
flexible and not requiring too many `pub`s. (In particular, I think it
would be nice if structs and enums, resp. struct-like and tuple-like bodies
for each, could be handled in a consistent fashion.)

[1]: https://github.com/mozilla/rust/issues/8122#issuecomment-31233466

So I was thinking that for both structs and enums, you could have either
all of their contents public or none. (With the potential exception of
individually public struct fields.) It makes no sense to have an enum with
some variants public and others private, nor is there much reason to
support enums with public variants and private fields. The vast majority of
the time, you want either a fully abstract datatype or a fully public one.
(Even with structs, if you have a single private field, you lose the
ability to construct and pattern match, and can only use dot syntax, so
it's closer to a fully-private type than to a fully-public one... but the
ability to control privacy per-field is kind of ingrained and the syntax is
easy enough, so there seems to be no harm in keeping it.)

Anyways, so that's similar to your idea. The hard part is coming up with
syntax. I don't like the name `class`: it has too many connotations, and
very few are ones we want. It *does* happen to have this meaning in C++,
but I think that's sort of accidental, and tends to surprise even C++ users
when they first learn of it (at least in my experience). In other languages
with a struct-class duality, like C# and D, it carries a lot more baggage.

I have a couple of ideas but am not truly satisfied with any of them. In
all cases the default would be that everything is private, and the question
is how to indicate otherwise:

// this was pnkfelix's idea
pub struct Point { pub: x: int, y: int }
pub struct Point(pub: int, int)
pub enum OptionT { pub: Some(T), None  }

// a minor variation putting the `pub` before the opening brace
pub struct Point pub: { x: int, y: int }
pub struct Point pub: (int, int)
pub enum OptionT pub: { Some(T), None  }

// use `{ .. }` to indicate the contents are also public:
pub { .. } struct Point { x: int, y: int }
// (a bit awkward if the contents don't use braces!)
pub { .. } struct Point(int, int)
pub { .. } enum OptionT { Some(T), None }

// a variation on the above
pub { pub } struct Point { x: int, y: int }
pub { pub } struct Point(int, int)
pub { pub } enum OptionT { Some(T), None }



 My first thought regarding variance was that we ought to say that
 any type which is not freeze is invariant with respect to its parameters,
 but that is really quite overly conservative. That means for example
 that something like

 struct MyVecT {
 r: RcVecT
 }

 is not covariant with respect to `T`, which strikes me as quite
 unfortunate! Rc, after all, is not freeze, but it's not freeze because
 of the ref count, not the data it points at.


...I still think it would be best to rely on visibility, if we want to
infer anything. (And for variance we probably do, otherwise almost
everything will end up with overconservative defaults.)

In terms of contracts, having public fields is a stronger contract than
stating a variance for a type parameter. You're stating that the type will
have *those exact fields*. Variance is merely a consequence. On the other
hand, if a field is private, the intent is to make no contract at all, so
nothing should be inferred from it.




 Some reasons I think it might be reasonable to treat variance differently:

 - Nobody but type theorists understands it, at least not at an
   intuitive level. It has been my experience that many, many very
   smart people just find it hard to grasp, so declaring variance
   explicitly is probably bad. Unfortunately, making all type
   parameters invariant is problematic when working with a type like
   `Option'a T`.

 

Re: [rust-dev] RFC: Opt-in builtin traits

2014-03-01 Thread Gareth Smith

On 01/03/14 07:23, Kevin Ballard wrote:

I'm also slightly concerned that #[deriving(Data)] gives the impression that 
there's a trait Data, so maybe that should be lowercased as in 
#[deriving(data)], or even just #[deriving(builtin)], but this is a lesser 
concern and somewhat bike-sheddy.

I guess that Data could actually be a declared as something like:

trait Data : Eq + Clone + ... {}

Gareth
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Opt-in builtin traits

2014-03-01 Thread Niko Matsakis
On Fri, Feb 28, 2014 at 10:36:11PM +0100, Gábor Lehel wrote:
 I don't see the difference here. Why do you think this should be handled
 differently? 

To be honest I hadn't thought about it before. I agree there is no
fundamental difference, though there may be a practical one. I have to
mull this over.

I think you're on to something in that there is a connection
public/private fields, but I'm not quite sure what that ought to
mean. I dislike the idea that adding a private field changes a whole
bunch of defaults. (I've at times floating the idea of having a
`struct`, where things are public-by-default and we auto-derive
various traits, and a `class`, where things are private-by-default and
everything is opt in, but nobody ever likes it but me. It's also
unclear how to treat enums in such a scheme.)

My first thought regarding variance was that we ought to say that
any type which is not freeze is invariant with respect to its parameters,
but that is really quite overly conservative. That means for example
that something like 

struct MyVecT {
r: RcVecT
}

is not covariant with respect to `T`, which strikes me as quite
unfortunate! Rc, after all, is not freeze, but it's not freeze because
of the ref count, not the data it points at.

Some reasons I think it might be reasonable to treat variance differently:

- Nobody but type theorists understands it, at least not at an
  intuitive level. It has been my experience that many, many very
  smart people just find it hard to grasp, so declaring variance
  explicitly is probably bad. Unfortunately, making all type
  parameters invariant is problematic when working with a type like
  `Option'a T`.

- It's unclear if the variance of type parameters is likely to evolve
  over time.

- It may not make much practical difference, since we don't have much
  subtyping in the language and it's likely to stay that way. I think
  right now subtyping is *only* induced via subtyping. The variable
  here is that if we introduced substruct types *and* subtyping rules
  there, subtyping might be more common.

  As an aside, I tried at one point to remove subtyping from the type
  inference altogether. This was working fine for a while but when I
  rebased the branch a while back I got lots of errors. I'm still tempted
  to try again.

- On the other hand, some part of me thinks we ought to just remove
  the variance inference and instead have a simple variance scheme: no
  annotation means covariant, otherwise you write `mut T` or `mut 'a`
  to declare an invariant parameter (intution being: a parameter must
  be invariant if it used within an interior mutable thing like a
  `Cell`). That would rather make this question moot.

Lots to think about!


Niko
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Opt-in builtin traits

2014-03-01 Thread Niko Matsakis
On Fri, Feb 28, 2014 at 11:23:23PM -0800, Kevin Ballard wrote:
 I'm also slightly concerned that #[deriving(Data)] gives the
 impression that there's a trait Data, so maybe that should be
 lowercased as in #[deriving(data)], or even just
 #[deriving(builtin)], but this is a lesser concern and somewhat
 bike-sheddy.

This is definitely bikeshedding, but that's important too. While we're
bikeshedding, I think we ought to rename the trait `Pod`, which
doesn't fit into our verb scheme:

   Freeze
   Send
   Share
   Pod

My first thought is to resurrect `Copy`, though of course it's very
similar to `Clone`. Anyway, let's assume for now we rename `Pod` to `Copy`.

In that case, I think I would have the following trait sets:

   data = Eq, Ord, Clone, Freeze, Hash, Share -- Note: not Copy!
   pod  = data, Copy

The idea is that almost all types (hashtables, etc) can use `data` (in
particular, `data` is applicable to types with `~` pointers).  Very
simple types like `Point` can use `pod` (which is, after all, just
plain old data).


Niko
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Opt-in builtin traits

2014-03-01 Thread Liigo Zhuang
I like this
2014年3月2日 上午4:07于 Niko Matsakis n...@alum.mit.edu写道:

 On Fri, Feb 28, 2014 at 11:23:23PM -0800, Kevin Ballard wrote:
  I'm also slightly concerned that #[deriving(Data)] gives the
  impression that there's a trait Data, so maybe that should be
  lowercased as in #[deriving(data)], or even just
  #[deriving(builtin)], but this is a lesser concern and somewhat
  bike-sheddy.

 This is definitely bikeshedding, but that's important too. While we're
 bikeshedding, I think we ought to rename the trait `Pod`, which
 doesn't fit into our verb scheme:

Freeze
Send
Share
Pod

 My first thought is to resurrect `Copy`, though of course it's very
 similar to `Clone`. Anyway, let's assume for now we rename `Pod` to `Copy`.

 In that case, I think I would have the following trait sets:

data = Eq, Ord, Clone, Freeze, Hash, Share -- Note: not Copy!
pod  = data, Copy

 The idea is that almost all types (hashtables, etc) can use `data` (in
 particular, `data` is applicable to types with `~` pointers).  Very
 simple types like `Point` can use `pod` (which is, after all, just
 plain old data).


 Niko
 ___
 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


Re: [rust-dev] RFC: Opt-in builtin traits

2014-02-28 Thread Matthieu Monrocq
I must admit I really like the *regularity* this brings to Rust. There is
nothing more difficult to reason about that an irregular (even if
reasonable) interface simply because one must keep all the rules in mind at
any time (oh and sorry, there is a special condition described at page 364
that applies to this precise usecase even though the specs sounds like it's
a universal rule).

Certainly, the annotation could be a burden, but #[deriving(Data)] is
extremely terse and brings in almost anything a user could need for its
type in one shot.

Finally, I believe the public API stability this brings is very necessary.
Too often incidental properties are relied upon and broken during updates
with the author not realizing it; when it's explicit, at least the library
author makes a conscious choice.


Maybe one way of preventing completely un-annotated pieces of data would be
a lint that just checks that at least one property (Send, Freeze, ...) or a
special annotation denoting their absence has been selected for each
public-facing type. By having a #[deriving(...)] mandatory, it makes it
easier for the lint pass to flag un-marked types without even having to
reason whether or not the type would qualify.

-- Matthieu



On Fri, Feb 28, 2014 at 4:51 PM, Niko Matsakis n...@alum.mit.edu wrote:

 From 
 http://smallcultfollowing.com/babysteps/blog/2014/02/28/rust-rfc-opt-in-builtin-traits/
 :

 ## Rust RFC: opt-in builtin traits

 In today's Rust, there are a number of builtin traits (sometimes
 called kinds): `Send`, `Freeze`, `Share`, and `Pod` (in the future,
 perhaps `Sized`). These are expressed as traits, but they are quite
 unlike other traits in certain ways. One way is that they do not have
 any methods; instead, implementing a trait like `Freeze` indicates
 that the type has certain properties (defined below). The biggest
 difference, though, is that these traits are not implemented manually
 by users. Instead, the compiler decides automatically whether or not a
 type implements them based on the contents of the type.

 In this proposal, I argue to change this system and instead have users
 manually implement the builtin traits for new types that they define.
 Naturally there would be `#[deriving]` options as well for
 convenience. The compiler's rules (e.g., that a sendable value cannot
 reach a non-sendable value) would still be enforced, but at the point
 where a builtin trait is explicitly implemented, rather than being
 automatically deduced.

 There are a couple of reasons to make this change:

 1. **Consistency.** All other traits are opt-in, including very common
traits like `Eq` and `Clone`. It is somewhat surprising that the
builtin traits act differently.
 2. **API Stability.** The builtin traits that are implemented by a
type are really part of its public API, but unlike other similar
things they are not declared. This means that seemingly innocent
changes to the definition of a type can easily break downstream
users. For example, imagine a type that changes from POD to non-POD
-- suddenly, all references to instances of that type go from
copies to moves. Similarly, a type that goes from sendable to
non-sendable can no longer be used as a message.  By opting in to
being POD (or sendable, etc), library authors make explicit what
properties they expect to maintain, and which they do not.
 3. **Pedagogy.** Many users find the distinction between pod types
(which copy) and linear types (which move) to be surprising. Making
pod-ness opt-in would help to ease this confusion.
 4. **Safety and correctness.** In the presence of unsafe code,
compiler inference is unsound, and it is unfortunate that users
must remember to opt out from inapplicable kinds. There are also
concerns about future compatibility. Even in safe code, it can also
be useful to impose additional usage constriants beyond those
strictly required for type soundness.

 I will first cover the existing builtin traits and define what they
 are used for. I will then explain each of the above reasons in more
 detail.  Finally, I'll give some syntax examples.

 !-- more --

  The builtin traits

 We currently define the following builtin traits:

 - `Send` -- a type that deeply owns all its contents.
   (Examples: `int`, `~int`, not `int`)
 - `Freeze` -- a type which is deeply immutable when accessed via an
   `T` reference.
   (Examples: `int`, `~int`, `int`, `mut int`, not `Cellint` or
`Atomicint`)
 - `Pod` -- plain old data which can be safely copied via memcpy.
   (Examples: `int`, `int`, not `~int` or `mut int`)

 We are in the process of adding an additional trait:

 - `Share` -- a type which is threadsafe when accessed via an `T`
   reference. (Examples: `int`, `~int`, `int`, `mut int`,
   `Atomicint`, not `Cellint`)

  Proposed syntax

 Under this proposal, for a struct or enum to be considered send,
 freeze, pod, etc, those traits must be explicitly 

Re: [rust-dev] RFC: Opt-in builtin traits

2014-02-28 Thread Niko Matsakis
On Fri, Feb 28, 2014 at 09:38:18PM +0100, Matthieu Monrocq wrote:
 The main issue with the lint as you proposed is that if I *want* a linear
 type without any property, the lint will continuously bug me. Thus the idea
 of #[deriving(None)] (or whatever name) to shut the lint down, because once
 you start ignoring warnings, you miss the important ones.

Lints can be disabled on a module-by-module basis.

```
#[allow(missing_traits)]
```


Niko
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Opt-in builtin traits

2014-02-28 Thread John Grosen
On Friday, February 28, 2014 at 11:15 AM, Matthieu Monrocq wrote:
 Maybe one way of preventing completely un-annotated pieces of data would be a 
 lint that just checks that at least one property (Send, Freeze, ...) or a 
 special annotation denoting their absence has been selected for each 
 public-facing type. By having a #[deriving(...)] mandatory, it makes it 
 easier for the lint pass to flag un-marked types without even having to 
 reason whether or not the type would qualify.
I generally like this idea; however, I find it a bit strange `deriving` would 
still be implemented as an attribute given its essential nature in the 
language. Haskell, of course, has `deriving` implemented as a first-class 
feature — might Rust be interested in something like that?

Food for thought, at least.

—
John Grosen

___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev


Re: [rust-dev] RFC: Opt-in builtin traits

2014-02-28 Thread Corey Richardson
(My idea for the lint was `#[allow_kind(Name)]`
, which someone on IRC remarked as opt-out opt-in builtin traits

On Fri, Feb 28, 2014 at 4:36 PM, Gábor Lehel glaebho...@gmail.com wrote:
 I think this is a really great idea.

 There's another potential compromise that would preserve most of its
 benefits, and reduce the annotation burden:

 There was another proposal earlier, driven by similar motivations, that
 structs with private fields should be non-`Pod`. Combining the two ideas, we
 could say that the built-in traits would be derived automatically for types
 with fully-public interiors, and would have to be declared or derived
 manually if any field is private. This would still accomplish what I think
 is the most important thing, which is to preserve abstraction boundaries:
 clients of a thing (type, module, package) should be insulated against
 changes to its private implementation. Relying on public information is, in
 this respect, however, fair game.

 The truly troublesome aspects of the current regime are, I believe, all
 consequences of the violation of abstraction boundaries. Types like `Cell`
 rely on these boundaries to ensure their safety, but under the current
 system, information about their private implementation leaks out. The OP and
 the above modification to it would both steer clear of this problem.

 I think the main tradeoffs between the two would be around simpler rules vs.
 fewer annotations, and the principle of least astonishment. This here idea
 is more complicated, because it has different rules for fully-public and
 abstract datatypes, and also (as currently) has different rules for built-in
 and user-defined traits. In exchange you only have to state your intentions
 explicitly if you have something to hide. The PoLA is harder to evaluate.
 Returning to the canonical example, if I write `struct Point { x: int, y:
 int }`, I think I'd be surprised if it weren't copyable. On the other hand,
 perhaps the afore-mentioned inconsistencies would also be surprising. So I
 dunno.

 This argument is rather weakened by the continued necessity of a
 `marker::InvariantType` marker. This could be read as an argument
 towards explicit variance. However, I think that in this particular
 case, the better solution is to introduce the `MutT` type described
 in #12577 -- the `MutT` type would give us the invariance.

 I don't see the difference here. Why do you think this should be handled
 differently? This is the same sort of abstraction boundary violation as the
 others: information about private fields is leaking out into the public
 interface via variance inference.

 Under the above scheme we could say that type parameters default to
 invariant for types with private fields, and are inferred for fully-public
 types. (How you would/could explicitly declare variance is another question,
 but kind of orthogonal to the idea that you /should/.)



 On Fri, Feb 28, 2014 at 4:51 PM, Niko Matsakis n...@alum.mit.edu wrote:

 From
 http://smallcultfollowing.com/babysteps/blog/2014/02/28/rust-rfc-opt-in-builtin-traits/:

 ## Rust RFC: opt-in builtin traits

 In today's Rust, there are a number of builtin traits (sometimes
 called kinds): `Send`, `Freeze`, `Share`, and `Pod` (in the future,
 perhaps `Sized`). These are expressed as traits, but they are quite
 unlike other traits in certain ways. One way is that they do not have
 any methods; instead, implementing a trait like `Freeze` indicates
 that the type has certain properties (defined below). The biggest
 difference, though, is that these traits are not implemented manually
 by users. Instead, the compiler decides automatically whether or not a
 type implements them based on the contents of the type.

 In this proposal, I argue to change this system and instead have users
 manually implement the builtin traits for new types that they define.
 Naturally there would be `#[deriving]` options as well for
 convenience. The compiler's rules (e.g., that a sendable value cannot
 reach a non-sendable value) would still be enforced, but at the point
 where a builtin trait is explicitly implemented, rather than being
 automatically deduced.

 There are a couple of reasons to make this change:

 1. **Consistency.** All other traits are opt-in, including very common
traits like `Eq` and `Clone`. It is somewhat surprising that the
builtin traits act differently.
 2. **API Stability.** The builtin traits that are implemented by a
type are really part of its public API, but unlike other similar
things they are not declared. This means that seemingly innocent
changes to the definition of a type can easily break downstream
users. For example, imagine a type that changes from POD to non-POD
-- suddenly, all references to instances of that type go from
copies to moves. Similarly, a type that goes from sendable to
non-sendable can no longer be used as a message.  By opting in to
being POD (or sendable, etc), library 

Re: [rust-dev] RFC: Opt-in builtin traits

2014-02-28 Thread Kevin Ballard
On Feb 28, 2014, at 8:10 PM, Kang Seonghoon some...@mearie.org wrote:

 2014-03-01 6:24 GMT+09:00 John Grosen jmgro...@gmail.com:
 On Friday, February 28, 2014 at 11:15 AM, Matthieu Monrocq wrote:
 
 Maybe one way of preventing completely un-annotated pieces of data would be
 a lint that just checks that at least one property (Send, Freeze, ...) or a
 special annotation denoting their absence has been selected for each
 public-facing type. By having a #[deriving(...)] mandatory, it makes it
 easier for the lint pass to flag un-marked types without even having to
 reason whether or not the type would qualify.
 
 I generally like this idea; however, I find it a bit strange `deriving`
 would still be implemented as an attribute given its essential nature in the
 language. Haskell, of course, has `deriving` implemented as a first-class
 feature — might Rust be interested in something like that?
 
 Food for thought, at least.
 
 I second to this. Indeed, we already have similar concerns about
 externally-implemented `#[deriving]` (#11813, and somewhat tangently,
 #11298), as syntax extensions don't have any clue about paths.

I actually rather like the fact that deriving is implemented as an attribute, 
because it's one less bit of syntax. Right now it's still implemented in the 
compiler, but this could theoretically eventually move into libstd entirely as 
a #[macro_registrar].

My main concern with this proposal overall is that types will forget to derive 
things. I know I almost always forget to derive Eq and Clone for my own structs 
until I run into an error due to their lack. A lint to warn about missing 
derivations would mitigate this a lot, although I'm worried that if someone 
opts out of a single trait by using #[allow(missing_traits)] and a new trait is 
added to the set, the author will never realize they're missing the new trait. 
I'm also concerned that if you need to opt out of a single trait from 
#[deriving(Data)] then you can't use #[deriving(Data)] and must instead list 
all of the remaining traits. Perhaps for both problems we could introduce the 
idea of #[deriving(!Send)], which would let me say 
#[deriving(Data,!Send,!Freeze)] to opt out of those two.

I'm also slightly concerned that #[deriving(Data)] gives the impression that 
there's a trait Data, so maybe that should be lowercased as in 
#[deriving(data)], or even just #[deriving(builtin)], but this is a lesser 
concern and somewhat bike-sheddy.

-Kevin
___
Rust-dev mailing list
Rust-dev@mozilla.org
https://mail.mozilla.org/listinfo/rust-dev