[rust-dev] Reminder: Bay Area meetup this Thursday
Hey rusties! I just wanted to remind everyone we have another meetup this Thursday, 7PM at Mozilla SF, where Julia Evans will talk about the OS she built with Rust. Here's the link to sign up: http://www.meetup.com/Rust-Bay-Area/events/166034142/ I hope you can make it! Erick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] Virtual fn is a bad idea
I see a proposal to add virtual struct and virtual fn in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust. I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of virtual functions (shared by Java's classes, etc.) is that rather than exposing a single public API, they expose two: the API formed by public functions, and the API formed by virtual functions to be overridden by subclasses, and the second API is exposed in an inflexible and unclean way. A much better way of allowing to override part of a struct's behavior is by defining a trait with the overridable functionality, and allowing to pass in an implementation of the trait to the base class, while also providing a default implementation if desired. Another way is to have the subclass implement all the traits that the base class implements, include a field of the base class type, and then direct all non-overridden functionality to the base class (here syntax sugar can be easily added to eliminate the boilerplate, by automatically implementing all non-implemented trait functions by calling the same function on the base class field). These approaches can be combined, as the first approach allows to change the inside behavior of the base class, while the second one allows to put extra behavior around the base class code. The fact that OO using virtual functions (as opposed to traits) is a bad design is one of the crucial steps forward of the design of languages like Go and current Rust compared to earlier OO languages, and Rust should not go backwards on this. Here is a list of issues with virtual functions: 1. Incentive for bad documentation Usually there is no documentation for how virtual functions are supposed to be overridden, and it as awkward to add it since it needs to be mixed with the documentation on how to use the struct 2. Mishmashing multiple unrelated APIs With traits, you could pass in multiple objects to implement separate sets of overridable functionality; with virtual structs you need to mishmash all those interfaces into a single set of virtual functions, all sharing data even when not appropriate. 3. No encapsulation Private data for virtual function implementations is accessible to all other functions in the struct. This means for instance that if you have a virtual function called compute_foo() that is implemented by default by reading a foo field in the base class, then all other parts of the base class can access foo too. If anything else accesses mistakenly foo directly, which it can, then overriding compute_foo() will not work as expected. If compute_foo() were provided by an external trait implementation, then foo would be private and inaccessible, eliminating the problem. 4. Data for overridden implementations left there in a zombie state. In the above example, if you override compute_foo(), the foo variable in the base class will no longer be used, yet it will still be present in the type and allocated in memory. 5. Inability to statically dispatch With a trait implementation, you can pass the concrete type as a generic parameter, allowing static dispatch. If you instead call an overridable virtual function, then you can't dispatch that statically at all (unless you add cumbersome syntax for that). 6. Adds a ton of unnecessary complexity to the language ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
On Tue, 2014-03-11 at 19:09 +, Bill Myers wrote: I see a proposal to add virtual struct and virtual fn in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust. I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of virtual functions (shared by Java's classes, etc.) is that rather than exposing a single public API, they expose two: the API formed by public functions, and the API formed by virtual functions to be overridden by subclasses, and the second API is exposed in an inflexible and unclean way. A much better way of allowing to override part of a struct's behavior is by defining a trait with the overridable functionality, and allowing to pass in an implementation of the trait to the base class, while also providing a default implementation if desired. Another way is to have the subclass implement all the traits that the base class implements, include a field of the base class type, and then direct all non-overridden functionality to the base class (here syntax sugar can be easily added to eliminate the boilerplate, by automatically implementing all non-implemented trait functions by calling the same function on the base class field). These approaches can be combined, as the first approach allows to change the inside behavior of the base class, while the second one allows to put extra behavior around the base class code. The fact that OO using virtual functions (as opposed to traits) is a bad design is one of the crucial steps forward of the design of languages like Go and current Rust compared to earlier OO languages, and Rust should not go backwards on this. Here is a list of issues with virtual functions: 1. Incentive for bad documentation Usually there is no documentation for how virtual functions are supposed to be overridden, and it as awkward to add it since it needs to be mixed with the documentation on how to use the struct 2. Mishmashing multiple unrelated APIs With traits, you could pass in multiple objects to implement separate sets of overridable functionality; with virtual structs you need to mishmash all those interfaces into a single set of virtual functions, all sharing data even when not appropriate. 3. No encapsulation Private data for virtual function implementations is accessible to all other functions in the struct. This means for instance that if you have a virtual function called compute_foo() that is implemented by default by reading a foo field in the base class, then all other parts of the base class can access foo too. If anything else accesses mistakenly foo directly, which it can, then overriding compute_foo() will not work as expected. If compute_foo() were provided by an external trait implementation, then foo would be private and inaccessible, eliminating the problem. 4. Data for overridden implementations left there in a zombie state. In the above example, if you override compute_foo(), the foo variable in the base class will no longer be used, yet it will still be present in the type and allocated in memory. 5. Inability to statically dispatch With a trait implementation, you can pass the concrete type as a generic parameter, allowing static dispatch. If you instead call an overridable virtual function, then you can't dispatch that statically at all (unless you add cumbersome syntax for that). 6. Adds a ton of unnecessary complexity to the language 7. Harder interoperability. For example I've encountered at least 2 Float interfaces for Java from 2 different libraries (both trying to abstract over Float) which I need to use in one of my projects (long story) - either one needed to have an adapter or there was a need to convert float to float. In total there were 3 32-bit floats representation in single function counting also the float. With traits they would just add implementation to Java's float. Best regards signature.asc Description: This is a digitally signed message part ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
On Tue, 2014-03-11 at 14:37 -0500, Evan G wrote: ... Why didn't they just extend Number? (Or, at worst, that was a bad design decision on Oracle's part, by choosing to make the Float class final) Either way, I don't see how that's a fault of the language. I don't remember - maybe they had, but it wouldn't solved the problem. And Float is final - and even if it hadn't been it wouldn't solve the problem at all. Assume that Float is not final and they did extended Number. - Platform P provides interface PI and object PO (say Number and Float) - Component A provided and required interface AI and object AO extending PO and implementing AI and PI - Component B provided and required interface BI and object AO extending PO and implementing BI and PI Now you cannot pass object AO to component B as it requires BI. You need either: - Provide adapter from AI to BI (or other way round) which implements AI, BI and PI - Each time convert from AO to BO when you transfer between interfaces In proposed interface there would be only second option due to single inheritance. On the other hand with traits: - Platform P provides interface PI and object PO - Component A provides and requires interface AI and implements AI for PO (there is no need for adding AI as PO is 'open' for trait implementation) - Component B provides and requires interface BI and implements BI for PO (there is no need for adding BI as PO is 'open' for trait implementation) The user needs to do: - Nothing. Everything works out of the box And before you ask - component A and B were 2 different libraries for which the Oracle interfaces were insufficient. Best regards On Tue, Mar 11, 2014 at 2:35 PM, Maciej Piechotka uzytkown...@gmail.com wrote: On Tue, 2014-03-11 at 19:09 +, Bill Myers wrote: I see a proposal to add virtual struct and virtual fn in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust. I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of virtual functions (shared by Java's classes, etc.) is that rather than exposing a single public API, they expose two: the API formed by public functions, and the API formed by virtual functions to be overridden by subclasses, and the second API is exposed in an inflexible and unclean way. A much better way of allowing to override part of a struct's behavior is by defining a trait with the overridable functionality, and allowing to pass in an implementation of the trait to the base class, while also providing a default implementation if desired. Another way is to have the subclass implement all the traits that the base class implements, include a field of the base class type, and then direct all non-overridden functionality to the base class (here syntax sugar can be easily added to eliminate the boilerplate, by automatically implementing all non-implemented trait functions by calling the same function on the base class field). These approaches can be combined, as the first approach allows to change the inside behavior of the base class, while the second one allows to put extra behavior around the base class code. The fact that OO using virtual functions (as opposed to traits) is a bad design is one of the crucial steps forward of the design of languages like Go and current Rust compared to earlier OO languages, and Rust should not go backwards on this. Here is a list of issues with virtual functions: 1. Incentive for bad documentation Usually there is no documentation for how virtual functions are supposed to be overridden, and it as awkward to add it since it needs to be mixed with the documentation on how to use the struct 2. Mishmashing multiple unrelated APIs With traits, you could pass in multiple objects to implement separate sets of overridable functionality; with virtual structs you need to mishmash all those interfaces into a single set of virtual functions, all sharing data even when not appropriate. 3. No encapsulation Private data for virtual function implementations is accessible to all other functions in the struct. This means for instance that if you have a virtual function called compute_foo() that is implemented by default by reading a foo field in the base class, then all other parts of the base
Re: [rust-dev] Virtual fn is a bad idea
I like virtual functions. They're not for everything, and stylistically, traits are almost always a better solution. However, they can be nice to reduce code bloat. See how the LLVM devs managed to share a good amount of code for their SmallVector class thanks to the magic of virtual functions: http://llvm.org/docs/doxygen/html/classllvm_1_1SmallVector.html Not sure if it deserves a whole keyword, but a way to do this efficiently would be nice. - Clark On Tue, Mar 11, 2014 at 3:51 PM, Maciej Piechotka uzytkown...@gmail.comwrote: On Tue, 2014-03-11 at 14:37 -0500, Evan G wrote: ... Why didn't they just extend Number? (Or, at worst, that was a bad design decision on Oracle's part, by choosing to make the Float class final) Either way, I don't see how that's a fault of the language. I don't remember - maybe they had, but it wouldn't solved the problem. And Float is final - and even if it hadn't been it wouldn't solve the problem at all. Assume that Float is not final and they did extended Number. - Platform P provides interface PI and object PO (say Number and Float) - Component A provided and required interface AI and object AO extending PO and implementing AI and PI - Component B provided and required interface BI and object AO extending PO and implementing BI and PI Now you cannot pass object AO to component B as it requires BI. You need either: - Provide adapter from AI to BI (or other way round) which implements AI, BI and PI - Each time convert from AO to BO when you transfer between interfaces In proposed interface there would be only second option due to single inheritance. On the other hand with traits: - Platform P provides interface PI and object PO - Component A provides and requires interface AI and implements AI for PO (there is no need for adding AI as PO is 'open' for trait implementation) - Component B provides and requires interface BI and implements BI for PO (there is no need for adding BI as PO is 'open' for trait implementation) The user needs to do: - Nothing. Everything works out of the box And before you ask - component A and B were 2 different libraries for which the Oracle interfaces were insufficient. Best regards On Tue, Mar 11, 2014 at 2:35 PM, Maciej Piechotka uzytkown...@gmail.com wrote: On Tue, 2014-03-11 at 19:09 +, Bill Myers wrote: I see a proposal to add virtual struct and virtual fn in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust. I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of virtual functions (shared by Java's classes, etc.) is that rather than exposing a single public API, they expose two: the API formed by public functions, and the API formed by virtual functions to be overridden by subclasses, and the second API is exposed in an inflexible and unclean way. A much better way of allowing to override part of a struct's behavior is by defining a trait with the overridable functionality, and allowing to pass in an implementation of the trait to the base class, while also providing a default implementation if desired. Another way is to have the subclass implement all the traits that the base class implements, include a field of the base class type, and then direct all non-overridden functionality to the base class (here syntax sugar can be easily added to eliminate the boilerplate, by automatically implementing all non-implemented trait functions by calling the same function on the base class field). These approaches can be combined, as the first approach allows to change the inside behavior of the base class, while the second one allows to put extra behavior around the base class code. The fact that OO using virtual functions (as opposed to traits) is a bad design is one of the crucial steps forward of the design of languages like Go and current Rust compared to earlier OO languages, and Rust should not go backwards on this. Here is a list of issues with virtual functions: 1. Incentive for bad documentation Usually there is no documentation for how virtual functions are supposed to be overridden, and it as awkward to add it since it needs to be mixed with the documentation on how to use the struct 2. Mishmashing multiple unrelated APIs With traits, you could pass in multiple objects to implement
Re: [rust-dev] Virtual fn is a bad idea
Bill's posting pleases me more than any other I have seen so far. Virtual functions in C++ have been an especially fruitful source of unfortunate consequences, both in the language and in users' programs. It is common in C++ coding guidelines to forbid public virtual functions, to help avoid mixing implementation details into a public interface, but that only mitigates one problem. Virtual functions have been a source of confusion, too, because they can be overloaded two different ways, with identical syntax but radically different semantics. The compiler cannot help catch mistakes because either might reasonably have been intended. This is part of the rationale for pure virtual classes, just so that failing to override a virtual as intended through some trivial error stands some chance of being noticed. For this reason, some coding guidelines go farther and _only_ allow overriding a parent's pure virtual functions. Virtual functions are the chief ingredient in what Alex Stepanov calls O-O gook. It should surprise no one that Java and C# went the wrong way by making all member functions virtual, thereby exposing all programs and all programmers to these ills all the time. That said, virtual functions do provide a more structured form of function pointer, which we do need. Any such feature should start with the problems it must solve, and work toward a defensible design, not by patching traditional O-O method overriding. Rust has the advantage over early C++ that lambdas and macros are available as well-defined building blocks. Ideally, Rust's architectural replacement for virtual functions would be purely a standard-library construct, demonstrating greater expressiveness to enable user code to do what another language is obliged to have built into its core. [aside: I don't know of any family connection to Bill.] Nathan Myers On 03/11/2014 12:09 PM, Bill Myers wrote: I see a proposal to add virtual struct and virtual fn in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust. I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of virtual functions (shared by Java's classes, etc.) is that rather than exposing a single public API, they expose two: the API formed by public functions, and the API formed by virtual functions to be overridden by subclasses, and the second API is exposed in an inflexible and unclean way. A much better way of allowing to override part of a struct's behavior is by defining a trait with the overridable functionality, and allowing to pass in an implementation of the trait to the base class, while also providing a default implementation if desired. Another way is to have the subclass implement all the traits that the base class implements, include a field of the base class type, and then direct all non-overridden functionality to the base class (here syntax sugar can be easily added to eliminate the boilerplate, by automatically implementing all non-implemented trait functions by calling the same function on the base class field). These approaches can be combined, as the first approach allows to change the inside behavior of the base class, while the second one allows to put extra behavior around the base class code. The fact that OO using virtual functions (as opposed to traits) is a bad design is one of the crucial steps forward of the design of languages like Go and current Rust compared to earlier OO languages, and Rust should not go backwards on this. Here is a list of issues with virtual functions: 1. Incentive for bad documentation Usually there is no documentation for how virtual functions are supposed to be overridden, and it as awkward to add it since it needs to be mixed with the documentation on how to use the struct 2. Mishmashing multiple unrelated APIs With traits, you could pass in multiple objects to implement separate sets of overridable functionality; with virtual structs you need to mishmash all those interfaces into a single set of virtual functions, all sharing data even when not appropriate. 3. No encapsulation Private data for virtual function implementations is accessible to all other functions in the struct. This means for instance that if you have a virtual function called compute_foo() that is implemented by default by reading a foo field in the base class, then all other parts of the base class can access foo too. If anything else accesses mistakenly foo directly, which it can, then overriding compute_foo() will not work as expected. If compute_foo() were provided by an external trait implementation, then foo would be private and inaccessible, eliminating the problem. 4. Data for overridden implementations left there in a zombie state. In the above example, if you override compute_foo(), the foo variable in the base class will no longer be used, yet it will
Re: [rust-dev] Virtual fn is a bad idea
See for example http://jscience.org/api/org/jscience/mathematics/structure/Field.html and http://jscience.org/api/org/jscience/mathematics/number/Float64.html. Now you cannot just use Double as to use http://jscience.org/api/org/jscience/mathematics/function/Polynomial.html you need to have a type that implements Ring. Then you needed to perform a few operations on polynomial and feed it to different library which had it own interfaces for float. You cannot just add static method as you're not an user of it - the other component is so using interface to abstract numeric operations, so function is not a viable workaround (BTW - I don't remember if jscience is one of libraries which I've used - they just appeared as one of first in Google). And sure, in ideal world all packages would have one interface but it wasn't something I controlled or anyone remotely connected with project. And I wasn't somehow inclined to rewrite 2 libraries. Another example would be interface which could be implemented in terms of another. So interface A is subset of B. You could make A a subinterface of B but it isn't and it is not something you control (for example first project don't want to depend on second, or does not know of its existence, or the work is in progress but it hasn't been done yet). If you happen to have control over whole system you're in much easier situation as you can make a single sane hierarchy. If you don't and you try to pull a few external libraries to work together as a small component of a larger project it's much more of a problem. Best regards On Tue, 2014-03-11 at 15:00 -0500, Evan G wrote: I still don't a hundred percent understand... what interface could there be that doesn't require the object to store the state necessary to implement it? I mean, anything else is really just a function, instead of 60.days_after(date) use days_after(60, date). On Tue, Mar 11, 2014 at 2:51 PM, Maciej Piechotka uzytkown...@gmail.com wrote: On Tue, 2014-03-11 at 14:37 -0500, Evan G wrote: ... Why didn't they just extend Number? (Or, at worst, that was a bad design decision on Oracle's part, by choosing to make the Float class final) Either way, I don't see how that's a fault of the language. I don't remember - maybe they had, but it wouldn't solved the problem. And Float is final - and even if it hadn't been it wouldn't solve the problem at all. Assume that Float is not final and they did extended Number. - Platform P provides interface PI and object PO (say Number and Float) - Component A provided and required interface AI and object AO extending PO and implementing AI and PI - Component B provided and required interface BI and object AO extending PO and implementing BI and PI Now you cannot pass object AO to component B as it requires BI. You need either: - Provide adapter from AI to BI (or other way round) which implements AI, BI and PI - Each time convert from AO to BO when you transfer between interfaces In proposed interface there would be only second option due to single inheritance. On the other hand with traits: - Platform P provides interface PI and object PO - Component A provides and requires interface AI and implements AI for PO (there is no need for adding AI as PO is 'open' for trait implementation) - Component B provides and requires interface BI and implements BI for PO (there is no need for adding BI as PO is 'open' for trait implementation) The user needs to do: - Nothing. Everything works out of the box And before you ask - component A and B were 2 different libraries for which the Oracle interfaces were insufficient. Best regards On Tue, Mar 11, 2014 at 2:35 PM, Maciej Piechotka uzytkown...@gmail.com wrote: On Tue, 2014-03-11 at 19:09 +, Bill Myers wrote: I see a proposal to add virtual struct and virtual fn in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust. I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of virtual functions (shared by Java's classes, etc.) is that rather than exposing a
Re: [rust-dev] Virtual fn is a bad idea
On 11/03/14 03:59 PM, Clark Gaebel wrote: I like virtual functions. They're not for everything, and stylistically, traits are almost always a better solution. However, they can be nice to reduce code bloat. See how the LLVM devs managed to share a good amount of code for their SmallVector class thanks to the magic of virtual functions: http://llvm.org/docs/doxygen/html/classllvm_1_1SmallVector.html Not sure if it deserves a whole keyword, but a way to do this efficiently would be nice. - Clark Traits already provide a choice between monomorphization and virtual function tables. I hope that the existing system can be extended in an orthogonal way. I would be very disappointed if Rust ended up like C++, where you have two almost completely exclusive systems. I think object inheritance is a very counter-intuitive concept, without much basis in either the real world or theory. Traits could be extended to include non-virtual fields, without us having the concept of base classes. Existing object systems like COM, DOM and gobject are worth looking at, but Rust shouldn't bend over backwards to support them. They're legacy technologies and while interacting with them is important, I don't think it should result in any extra complexity being added to Rust. signature.asc Description: OpenPGP digital signature ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
On 3/11/14 1:42 PM, Daniel Micay wrote: Existing object systems like COM, DOM and gobject are worth looking at, but Rust shouldn't bend over backwards to support them. They're legacy technologies and while interacting with them is important, I don't think it should result in any extra complexity being added to Rust. We have to support the technologies that are in use in a pleasant way, or else Rust will not be practical. Regardless of your feelings about existing OO systems, Rust has to support them well. So far nobody in this thread has demonstrated an understanding of the constraints here. Traits are simply not sufficient to model the DOM, for example. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
On 3/11/14 12:09 PM, Bill Myers wrote: I see a proposal to add virtual struct and virtual fn in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust. I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of virtual functions (shared by Java's classes, etc.) is that rather than exposing a single public API, they expose two: the API formed by public functions, and the API formed by virtual functions to be overridden by subclasses, and the second API is exposed in an inflexible and unclean way. A much better way of allowing to override part of a struct's behavior is by defining a trait with the overridable functionality, and allowing to pass in an implementation of the trait to the base class, while also providing a default implementation if desired. This approach is not efficient enough for use cases like the DOM (two words on each pointer instead of one, for example), and doesn't allow for nonvirtual access of fields through a trait object. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
The downsides you list are all more or less applicable to this design, indeed. We are seeing real requirements in real code that indicates that the current abstraction facilities provided by Rust are efficient enough for certain demanding use cases (the DOM in particular). Here are the identified requirements: tree of types (single inheritance) downcasting thin pointers cheap field access easy upcasting The big problem that putting virtual methods on structs solves is removing the need for fat pointers to objects - with a lot of trait objects this can involve many duplicated words. Fortunately, this feature is independent of others and we can feature gate it until it's right. So as with many aspects of Rust's design we are going to iterate on it using *real code* until it's right, and in the end Rust is going to be able to to provide the zero-cost abstractions necessary to write very high performance code. Regards, Brian On 03/11/2014 12:09 PM, Bill Myers wrote: I see a proposal to add virtual struct and virtual fn in the workweek meeting notes, which appears to add an exact copy of Java's OO system to Rust. I think however that this should be carefully considered, and preferably not added at all (or failing that, feature gated and discouraged). The core problem of virtual functions (shared by Java's classes, etc.) is that rather than exposing a single public API, they expose two: the API formed by public functions, and the API formed by virtual functions to be overridden by subclasses, and the second API is exposed in an inflexible and unclean way. A much better way of allowing to override part of a struct's behavior is by defining a trait with the overridable functionality, and allowing to pass in an implementation of the trait to the base class, while also providing a default implementation if desired. Another way is to have the subclass implement all the traits that the base class implements, include a field of the base class type, and then direct all non-overridden functionality to the base class (here syntax sugar can be easily added to eliminate the boilerplate, by automatically implementing all non-implemented trait functions by calling the same function on the base class field). These approaches can be combined, as the first approach allows to change the inside behavior of the base class, while the second one allows to put extra behavior around the base class code. The fact that OO using virtual functions (as opposed to traits) is a bad design is one of the crucial steps forward of the design of languages like Go and current Rust compared to earlier OO languages, and Rust should not go backwards on this. Here is a list of issues with virtual functions: 1. Incentive for bad documentation Usually there is no documentation for how virtual functions are supposed to be overridden, and it as awkward to add it since it needs to be mixed with the documentation on how to use the struct 2. Mishmashing multiple unrelated APIs With traits, you could pass in multiple objects to implement separate sets of overridable functionality; with virtual structs you need to mishmash all those interfaces into a single set of virtual functions, all sharing data even when not appropriate. 3. No encapsulation Private data for virtual function implementations is accessible to all other functions in the struct. This means for instance that if you have a virtual function called compute_foo() that is implemented by default by reading a foo field in the base class, then all other parts of the base class can access foo too. If anything else accesses mistakenly foo directly, which it can, then overriding compute_foo() will not work as expected. If compute_foo() were provided by an external trait implementation, then foo would be private and inaccessible, eliminating the problem. 4. Data for overridden implementations left there in a zombie state. In the above example, if you override compute_foo(), the foo variable in the base class will no longer be used, yet it will still be present in the type and allocated in memory. 5. Inability to statically dispatch With a trait implementation, you can pass the concrete type as a generic parameter, allowing static dispatch. If you instead call an overridable virtual function, then you can't dispatch that statically at all (unless you add cumbersome syntax for that). 6. Adds a ton of unnecessary complexity to the language ___ 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] Virtual fn is a bad idea
On 3/11/14 1:42 PM, Daniel Micay wrote: Traits already provide a choice between monomorphization and virtual function tables. I hope that the existing system can be extended in an orthogonal way. I would be very disappointed if Rust ended up like C++, where you have two almost completely exclusive systems. This is as orthogonal of a way as we could come up with while maintaining the constraints of (a) one pointer, not two; (b) statically accessed fields at known offsets through trait objects; (c) the prefix property for single inheritance. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
On 03/11/2014 04:52 PM, Brian Anderson wrote: Fortunately, this feature is independent of others and we can feature gate it until it's right. I think that's the crux of the issue some have with this. If a whole another, completely disjoint path for inheritance and dynamic polymorphism is required for the sake of efficiency, then maybe trait objects should go? -SL ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
On Tue, 2014-03-11 at 13:44 -0700, Patrick Walton wrote: On 3/11/14 1:42 PM, Daniel Micay wrote: Existing object systems like COM, DOM and gobject are worth looking at, but Rust shouldn't bend over backwards to support them. They're legacy technologies and while interacting with them is important, I don't think it should result in any extra complexity being added to Rust. We have to support the technologies that are in use in a pleasant way, or else Rust will not be practical. Regardless of your feelings about existing OO systems, Rust has to support them well. So far nobody in this thread has demonstrated an understanding of the constraints here. Traits are simply not sufficient to model the DOM, for example. Patrick Could you elaborate on DOM? I saw it referred a few times but I haven't seen any details. I wrote simple bindings to libxml2 dom (https://github.com/uzytkownik/xml-rs - warning - I wrote it while I was learning ruby) and I don't think there was a problem of OO - main problem was mapping libxml memory management and rust's one [I gave up with namespaces but with native rust dom implementation it would be possible to solve in nicer way]. Of course - I might've been at too early stage. Regarding existing OO systems - Haskell interops with few of them (like gtk+ for example) using typeclasses without problems I know of. Possible next stage would be modelling the same hierarchy but since most systems use multiple inheritance in one form or another it would not help much. Best regards signature.asc Description: This is a digitally signed message part ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
On 3/11/14 2:09 PM, SiegeLord wrote: On 03/11/2014 04:52 PM, Brian Anderson wrote: Fortunately, this feature is independent of others and we can feature gate it until it's right. I think that's the crux of the issue some have with this. If a whole another, completely disjoint path for inheritance and dynamic polymorphism is required for the sake of efficiency, then maybe trait objects should go? You still need them for Java `interface`-like functionality, where you need to step outside the boundaries of single inheritance. Single inheritance alone is too limiting. You can do C++-like multiple inheritance in theory with thin pointers, but you lose runtime efficiency doing it that way (double dispatch for finding fields) and in practice people don't use multiple inheritance with the `virtual` in C++ very often for this reason. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
On 3/11/14 2:15 PM, Maciej Piechotka wrote: Could you elaborate on DOM? I saw it referred a few times but I haven't seen any details. I wrote simple bindings to libxml2 dom (https://github.com/uzytkownik/xml-rs - warning - I wrote it while I was learning ruby) and I don't think there was a problem of OO - main problem was mapping libxml memory management and rust's one [I gave up with namespaces but with native rust dom implementation it would be possible to solve in nicer way]. Of course - I might've been at too early stage. You need: 1. One-word pointers to each DOM node, not two. Every DOM node has 5 pointers inside (parent, first child, last child, next sibling, previous sibling). Using trait objects would 10 words, not 5 words, and would constitute a large memory regression over current browser engines. 2. Access to fields common to every instance of a trait without virtual dispatch. Otherwise the browser will be at a significant performance disadvantage relative to other engines. 3. Downcasting and upcasting. 4. Inheritance with the prefix property, to allow for (2). If anyone has alternative proposals that handle these constraints that are more orthogonal and are pleasant to use, then I'm happy to hear them. I'm just saying that dismissing the feature out of hand is not productive. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
FWIW, C# requires that you mark overridable functions *virtual*, the opposite of Java where you need to mark un-overridable functions *final*. The Scala community is coming to the same conclusion that unrestricted overriding is pretty dangerous. It's similar to monkey patching, with all the convenience and danger it provides. On Tue, Mar 11, 2014 at 2:15 PM, Maciej Piechotka uzytkown...@gmail.comwrote: On Tue, 2014-03-11 at 13:44 -0700, Patrick Walton wrote: On 3/11/14 1:42 PM, Daniel Micay wrote: Existing object systems like COM, DOM and gobject are worth looking at, but Rust shouldn't bend over backwards to support them. They're legacy technologies and while interacting with them is important, I don't think it should result in any extra complexity being added to Rust. We have to support the technologies that are in use in a pleasant way, or else Rust will not be practical. Regardless of your feelings about existing OO systems, Rust has to support them well. So far nobody in this thread has demonstrated an understanding of the constraints here. Traits are simply not sufficient to model the DOM, for example. Patrick Could you elaborate on DOM? I saw it referred a few times but I haven't seen any details. I wrote simple bindings to libxml2 dom (https://github.com/uzytkownik/xml-rs - warning - I wrote it while I was learning ruby) and I don't think there was a problem of OO - main problem was mapping libxml memory management and rust's one [I gave up with namespaces but with native rust dom implementation it would be possible to solve in nicer way]. Of course - I might've been at too early stage. Regarding existing OO systems - Haskell interops with few of them (like gtk+ for example) using typeclasses without problems I know of. Possible next stage would be modelling the same hierarchy but since most systems use multiple inheritance in one form or another it would not help much. Best regards ___ 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] Virtual fn is a bad idea
On 3/11/14 2:19 PM, Haoyi Li wrote: FWIW, C# requires that you mark overridable functions *virtual*, the opposite of Java where you need to mark un-overridable functions *final*. The Scala community is coming to the same conclusion that unrestricted overriding is pretty dangerous. It's similar to monkey patching, with all the convenience and danger it provides. As a systems language we'd definitely need to keep virtual as non-default anyhow. Patrick ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
I can't help but feel that forcing the single inheritance of fast field access and inheritance of trait functions into one mechanism would be regrettable. Would https://github.com/mozilla/rust/issues/10491 address all the requirements? If not, why? On Tue, Mar 11, 2014 at 10:52 PM, Brian Anderson bander...@mozilla.comwrote: The downsides you list are all more or less applicable to this design, indeed. We are seeing real requirements in real code that indicates that the current abstraction facilities provided by Rust are efficient enough for certain demanding use cases (the DOM in particular). Here are the identified requirements: tree of types (single inheritance) downcasting thin pointers cheap field access easy upcasting ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
By the way, I didn't see any discussion of the HasPrefix/Coercible proposalhttps://github.com/mozilla/rust/issues/9912#issuecomment-36073562in the workweek minutes. Did anybody bring it up at all? Vadim On Tue, Mar 11, 2014 at 2:30 PM, Oren Ben-Kiki o...@ben-kiki.org wrote: I can't help but feel that forcing the single inheritance of fast field access and inheritance of trait functions into one mechanism would be regrettable. Would https://github.com/mozilla/rust/issues/10491 address all the requirements? If not, why? On Tue, Mar 11, 2014 at 10:52 PM, Brian Anderson bander...@mozilla.comwrote: The downsides you list are all more or less applicable to this design, indeed. We are seeing real requirements in real code that indicates that the current abstraction facilities provided by Rust are efficient enough for certain demanding use cases (the DOM in particular). Here are the identified requirements: tree of types (single inheritance) downcasting thin pointers cheap field access easy upcasting ___ 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] Virtual fn is a bad idea
I've actually come around and now I'm starting to actually like this virtual struct proposal, although not necessarily the example syntax (yay bikeshed!). What brought me around is that if you squint your eyes a bit, the example was mashing together struct inheritance and virtual methods into one syntax. If we decouple the two we can get a more rust-y syntax (to me) if we reify the anonymous trait into something that can be implemented by substructures: ``` // Unsized structs are unsized but *can't* end with an unsized field (so they can be extended) unsized struct Base { id: int } unsized struct Foo: Base { } struct Bar: Foo { } impl Base { fn a(); // impls can now have unimplemented methods fn b() { } } impl Base for Foo { fn a() { } } impl Foo { fn c() { } } impl Base for Bar {} impl Foo for Bar {} fn do_somethingT: Base(t: Base) { println!({}, t.id); // compiler can figure out the exact field offset because `Base` is a struct trait } fn main() { let bases = ~[ ~Foo { id: 1 } as ~Base, // compiler uses the more optimal `~(*vtable, obj)` because `Base` is a struct trait ~Bar { id: 2 } as ~Base, ]; for base in bases.iter() { do_something(base); } } ``` One interesting thing we could do with this is allow named traits to also derive from struct traits. This could allow parallel virtual method trees: ``` unsized struct Base { id: int } unsized struct Foo: Base { } struct Bar: Foo { } trait BaseTrait1: Base { fn foo(self) { println!({}, self.id); // exact offset because id comes from a struct trait } } impl BaseTrait1 for Base { } impl BaseTrait1 for Foo { } impl BaseTrait1 for Bar { } trait BaseTrait2: Base { fn bar(self) { println!({}, self.id); // exact offset because id comes from a struct trait } } impl BaseTrait2 for Base { } impl BaseTrait2 for Foo { } impl BaseTrait2 for Bar { } fn main() { let bases = ~[ ~Foo as ~BaseTrait1, ~Bar as ~BaseTrait1 ]; // compiler uses more optimal ~(*vtable, obj)` layout for base in bases.iter() { base.foo() } let bases = ~[ ~Foo as ~BaseTrait2, ~Bar as ~BaseTrait2 ]; for base in bases.iter() { base.foo() } } ``` I'm not sure if there are any practical applications for this though. ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
[rust-dev] RFC: Updated RFC process
Hey, Rusties. The freewheeling way that we add new features to Rust has been good for early development, but for Rust to become a mature platform we need to develop some more self-discipline when it comes to changing the system. So this is a proposed modification to our current RFC process to make it a more integral part of the overall development process, and one that is followed consistently to introduce features to Rust. Some improvements I would like this to bring over the current process include: * Discourage unactionable or vague RFCs * Ensure that all serious RFCs are considered equally * Those with a stake in Rust's development should feel confident they understand why new features are being merged Below is the proposed change to the RFC process. Please read and comment. Assuming the feedback is positive I'll update the wiki with this later. - Many changes, including bug fixes and documentation improvements can be implemented and reviewed via the normal GitHub pull request workflow. Some changes though are substantial, and we ask that these be put through a bit of a design process and produce a consensus among the Rust community and the [core team]. The RFC (request for comments process) is intended to provide a consistent and controlled path for new features to enter the language and standard libraries, so that all stakeholders can be confident about the direction the language is evolving in. ## When you need to follow this process You need to follow this process if you intend to make substantial changes to the Rust distribution. What constitutes a substantial change is evolving based on community norms, but may include the following. - Any semantic or syntactic change to the language that is not a bugfix. - Changes to the interface between the compiler and libraries, including lang items and intrinsics. - Additions to `std` Some changes do not require an RFC: - Rephrasing, reorganizing, refactoring, or otherwise changing shape does not change meaning. - Additions that strictly improve objective, numerical quality criteria (warning removal, speedup, better platform coverage, more parallelism, trap more errors, etc.) - Additions only likely to be _noticed by_ other developers-of-rust, invisible to users-of-rust. If you submit a pull request to implement a new feature without going through the RFC process, it may be closed with a polite request to submit an RFC first. ## What the process is In short, to get a major feature added to Rust, one must first get the RFC merged into the RFC repo as a markdown file. At that point the RFC is 'active' and may be implemented with the goal of eventual inclusion into Rust. * Fork the RFC repo http://github.com/rust-lang/rfcs * Copy `-template.md` to `active/-my-feature.md` (where 'my-feature' is descriptive. don't assign an RFC number yet). * Fill in the RFC * Submit a pull request. The pull request is the time to get review of the design from the larger community. * Build consensus and integrate feedback. RFCs that have broad support are much more likely to make progress than those that don't receive any comments. * Eventually, somebody on the [core team] will either accept the RFC by merging the pull request and assigning the RFC a number, at which point the RFC is 'active', or reject it by closing the pull request. Once an RFC becomes active then authors may implement it and submit the feature as a pull request to the Rust repo. An 'active' is not a rubber stamp, and in particular still does not mean the feature will ultimately be merged; it does mean that in principle all the major stakeholders have agreed to the feature and are amenable to merging it. Modifications to active RFC's can be done in followup PR's. An RFC that makes it through the entire process to implementation is considered 'complete' and is moved to the 'complete' folder; an RFC that fails after becoming active is 'inactive' and moves to the 'inactive' folder. ### Help this is all too informal! The process is intended to be as lightweight as reasonable for the present circumstances. As usual, we are trying to let the process be driven by consensus and community norms, not impose more structure than necessary. [core team]: https://github.com/mozilla/rust/wiki/Note-core-team ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Doc sprint planning
Thanks for this. This sounds great. We should form a group on Thursday at the meetup and come up with that checklist. I can help track down the doc-less APIs Friday night and some of Saturday too. On Mon, Mar 10, 2014 at 5:38 PM, Brian Anderson bander...@mozilla.comwrote: Hey, As you may know, we've got our first doc sprint scheduled for Sunday, 12-4 Pacific time. We'll to set up the commons area at the SF office with pizza and turn on video conferencing for remoties. Before that day comes though we need to come up with some kind of plan, something that is simple and fun. Here's my strawman proposal for what we're going to do Sunday: We'll focus on API docs since those are bitesize units of work and extremely parallelizable. Before the event we come up with a checklist for what API docs should include, as well as a list of high-value API's with missing docs. Sunday we put that list on an etherpad, and let people claim individual functions, traits, etc. Additionally, we keep a 'scoreboard' to track who completes the most docs. Whoever has the most docs at the end wins (just pizza and accolades). Does this sound reasonable? Does anybody want to volunteer to 'run' the sprint (make the list of needed docs, help people find tasks, collect and upstream the results, etc.)? Regards, Brian ___ 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] Virtual fn is a bad idea
On Tue, 2014-03-11 at 14:18 -0700, Patrick Walton wrote: On 3/11/14 2:15 PM, Maciej Piechotka wrote: Could you elaborate on DOM? I saw it referred a few times but I haven't seen any details. I wrote simple bindings to libxml2 dom (https://github.com/uzytkownik/xml-rs - warning - I wrote it while I was learning ruby) and I don't think there was a problem of OO - main problem was mapping libxml memory management and rust's one [I gave up with namespaces but with native rust dom implementation it would be possible to solve in nicer way]. Of course - I might've been at too early stage. You need: 1. One-word pointers to each DOM node, not two. Every DOM node has 5 pointers inside (parent, first child, last child, next sibling, previous sibling). Using trait objects would 10 words, not 5 words, and would constitute a large memory regression over current browser engines. 2. Access to fields common to every instance of a trait without virtual dispatch. Otherwise the browser will be at a significant performance disadvantage relative to other engines. 3. Downcasting and upcasting. 4. Inheritance with the prefix property, to allow for (2). If anyone has alternative proposals that handle these constraints that are more orthogonal and are pleasant to use, then I'm happy to hear them. I'm just saying that dismissing the feature out of hand is not productive. Patrick Ok. I see where my misunderstanding was - I was thinking about DOM implementation in Ruby for Ruby while you (Mozilla) were talking about implementation in Ruby for JavaScript. Please feel free to ignore next paragraph as I haven't given it much though but my guess would be that it would be possible to avoid the penalty by enum + alignment + smart compiler. As data have 6 words + in your scheme (5 described + vtable) the 4 bit alignment (assuming 32+ bit platform) should not cause much memory waste. This allows for using the 'wasted' bits for other purposes (the trick is used in, for example, lock-free structures) - for example: enum ElementChild'r { ElementChildElement('r Element), ElementChildComment('r Comment), } Could be represented as pointer with last bit specifying if it's element or comment - similar to OptionT optimization. The lookup should behave much nicer with respect to branch prediction (plus) but getting pointer is more complicated (minus) and possibly longer code instead of jump (minus). If data alignes the compiler should be able to optimize the jumps if it notices that all jumps lead to the same pointer arithmetic. I'm not sure about handles from JS but I don't think there is more then 16 choices for types of parent/child/sibling for any node so it should be achivable - on language side it would just be enum + pointer (+ specification of alignment as attribute?). That said a) I have done no measurements/benchmarks so my intuition is likely to be wrong b) should in above paragraph means 'it looks that it could work after 5s thought' and c) I'm not a Rust designer and I don't pay for nor contribute so I don't expect that I should have anything resembling last word. Best regards signature.asc Description: This is a digitally signed message part ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev
Re: [rust-dev] Virtual fn is a bad idea
I thought about tricks like this, but (a) a sufficiently smart compiler should *not* be doing this automatically, as it makes certain common operations like fetching the pointer more expensive as well as violating the principle of least surprise when it comes to machine representation, and (b) does this sort of hack really result in a cleaner design than having some simple language extensions? Mind you, I'm all about ways to simplify Rust, but sometimes the simplest solution is to build stuff into the language. On March 11, 2014 7:39:30 PM PDT, Maciej Piechotka uzytkown...@gmail.com wrote: On Tue, 2014-03-11 at 14:18 -0700, Patrick Walton wrote: On 3/11/14 2:15 PM, Maciej Piechotka wrote: Could you elaborate on DOM? I saw it referred a few times but I haven't seen any details. I wrote simple bindings to libxml2 dom (https://github.com/uzytkownik/xml-rs - warning - I wrote it while I was learning ruby) and I don't think there was a problem of OO - main problem was mapping libxml memory management and rust's one [I gave up with namespaces but with native rust dom implementation it would be possible to solve in nicer way]. Of course - I might've been at too early stage. You need: 1. One-word pointers to each DOM node, not two. Every DOM node has 5 pointers inside (parent, first child, last child, next sibling, previous sibling). Using trait objects would 10 words, not 5 words, and would constitute a large memory regression over current browser engines. 2. Access to fields common to every instance of a trait without virtual dispatch. Otherwise the browser will be at a significant performance disadvantage relative to other engines. 3. Downcasting and upcasting. 4. Inheritance with the prefix property, to allow for (2). If anyone has alternative proposals that handle these constraints that are more orthogonal and are pleasant to use, then I'm happy to hear them. I'm just saying that dismissing the feature out of hand is not productive. Patrick Ok. I see where my misunderstanding was - I was thinking about DOM implementation in Ruby for Ruby while you (Mozilla) were talking about implementation in Ruby for JavaScript. Please feel free to ignore next paragraph as I haven't given it much though but my guess would be that it would be possible to avoid the penalty by enum + alignment + smart compiler. As data have 6 words + in your scheme (5 described + vtable) the 4 bit alignment (assuming 32+ bit platform) should not cause much memory waste. This allows for using the 'wasted' bits for other purposes (the trick is used in, for example, lock-free structures) - for example: enum ElementChild'r { ElementChildElement('r Element), ElementChildComment('r Comment), } Could be represented as pointer with last bit specifying if it's element or comment - similar to OptionT optimization. The lookup should behave much nicer with respect to branch prediction (plus) but getting pointer is more complicated (minus) and possibly longer code instead of jump (minus). If data alignes the compiler should be able to optimize the jumps if it notices that all jumps lead to the same pointer arithmetic. I'm not sure about handles from JS but I don't think there is more then 16 choices for types of parent/child/sibling for any node so it should be achivable - on language side it would just be enum + pointer (+ specification of alignment as attribute?). That said a) I have done no measurements/benchmarks so my intuition is likely to be wrong b) should in above paragraph means 'it looks that it could work after 5s thought' and c) I'm not a Rust designer and I don't pay for nor contribute so I don't expect that I should have anything resembling last word. Best regards ___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev -- Sent from my Android phone with K-9 Mail. Please excuse my brevity.___ Rust-dev mailing list Rust-dev@mozilla.org https://mail.mozilla.org/listinfo/rust-dev