Re: [rust-dev] Virtual fn is a bad idea

2014-03-12 Thread Maciej Piechotka
Recently I saw a video of person from scala team who regretted some
'pragmatic' choices in long run so I might be still under it's
impression regarding pragmatic choices in a language. Fortunately (or
unfortunately) I'm not in charge of Rust design.

Also last question - why not use:

struct Element {
//... element specific elements
}

struct Attribute {
//... attribute specific elements
}

enum NodeChild {
NodeAttribute(Attribute),
NodeElement(Element)
}

struct Node'r {
parent: 'r Node,
first_child: 'r Node,
last_child: 'r Node,
next_sibling: 'r Node,
last_sibling: 'r Node,
node: NodeChild
}

For static types:

struct Element'r {
elem: 'r Node
}

pub fn downcast_to_element'r(node: 'r Node) - OptionElement'r {
match (node.node) {
NodeElement(_) - Some(Element {elem: node}),
_ - None
}
}

+ impl with potentially partial methods? Is the 'all nodes consuming
same amount of memory' too much memory?

1. One word pointers - check
2. Access to fields - check
3. Downcasting and upcasting - check
4. Inheritance with prefix property - check (sort of)
5. No need for behind-the-back optimization - check

-. static dispatch (check) at the cost of match instead of jump with all
its pros and cons

Best regards

On Tue, 2014-03-11 at 19:47 -0700, Patrick Walton wrote:
 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

Re: [rust-dev] Virtual fn is a bad idea

2014-03-11 Thread Maciej Piechotka
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

2014-03-11 Thread Maciej Piechotka
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

2014-03-11 Thread Maciej Piechotka
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

Re: [rust-dev] Virtual fn is a bad idea

2014-03-11 Thread Maciej Piechotka
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

2014-03-11 Thread Maciej Piechotka
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] About RFC: A 30 minute introduction to Rust

2014-03-05 Thread Maciej Piechotka
On Tue, 2014-03-04 at 23:54 -0300, Fernando Pelliccioni wrote:

 We still have the problem of dangling references.
 
 
 Any decent compiler can deal with this problem, and according to the
 Standard the implementations are encouraged to issue a warning in such
 a case.
 I don't know implementations that don't do it.
 
 
 GCC:
 In function 'int dangling()':
 warning: reference to local variable 'i' returned
 [-Wreturn-local-addr]
 int i = 1234;
 ^
 Clang
 warning: reference to stack memory associated with local variable
 'i' returned [-Wreturn-stack-address]
 return i;
^
 
 
 The same happens with MSVC.
 
 
 Do you think that the Warning is not much? 
 Well, you can use a compiler option
 
 
 -Werror
 Or in this case
 -Werror-return-stack-address
 
 
 ...and... voilĂ , the problem is over, we now have errors instead of
 warnings.
 What is the advantage of Rust now? I think it's insignificant.
 


I usually find myself defending C++ in most discussion but it's not
ideal (although from what I heard about article it is much less horrible
then presented).

#include iostream

class Test {
public:
Test(int i) : i(i) {}
int get_i() const {
i++
return i;
}
private:
int i;
};

Test foo(int i) {
return Test(i);
}

int main() { 
Test a = foo(1);
Test b = foo(2);
std::cout  a.get_i() b.get_i()  std::endl;
return 0;
}

No warnings on -Wall -Wextra -O1. Of course -O1 and -O0 have different
results as we are happy to write somewhere on stack and in first case
the valgrind does not report any errors. Add more code, foo begin a bit
longer (or getting itself a reference) multiple files, Cheshire Cats...

Once you get any structure more complicated then tree you'll need to use
pointers. You can use std::shared_pointer as long as your structure is
DAG - once it isn't you are on your own. 'Rust' have similar problem -
but it has compile error instead of potentially a warning (potentially
as C++ needs to have 'sufficient' knowledge).

(Not mentioning the reference invalidation mentioned earlier)

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