You are correct that using a virtual table does incur some runtime cost.  That's one of the reasons we chose to offer the option of using bounded generics as an alternative, since (due to monomorphization) this implies no vtable.  For example:

    trait SomeTrait { fn method(); }
 
    // Uses a vtable: dynamic dispatch
    fn foo(x: &SomeTrait) { x.method(); }

    // Does not use a vtable: static dispatch
    fn foo<T: SomeTrait>(x: &T) { x.method(); }
 
So in your inner loop, you may want to stick to bounded generics.  (Of course, it would be possible for a C++ or Rust compiler to emit code with PICs.  However, that implies a rather larger runtime, and we are generally shooting for less runtime.)

I am not sure which "OO issues" you are referring to from that e-mail precisely, but I do believe our design addresses many (if not all) of the points that Shapiro raised. 


Niko
October 10, 2012 10:22 PM
Niko , what about the performance of these polymorphic functions ? Even C++ virtual calls bite when you have calls to a different compilation unit compared to a good java run time ( mainly due to polymorphic inline caches).
And how do you handle module traits there are some nasty issue here  with Haskell. 

You still confident you can avoid the OO issues bitC hit when they tried to add OO calls to traits eg


Ben


_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev
October 10, 2012 9:14 PM
For what it's worth, Sebastian, Simon Peyton Jones agrees with you:

    http://research.microsoft.com/en-us/um/people/simonpj/papers/haskell-retrospective/

(Skip ahead to page 56, he talks specifically about its use in intellisense, calling it "the power of the dot").

To me, there is another very important point regarding dot notation that I didn't see mentioned yet: it enables name resolution on a per-type basis.  In Haskell, if I want to have to have something like "foo.context()" and "bar.context()" where the context of a foo has one type and bar has another, it's a big pain and I have to use import qualified or something crazy like that.  In OOP this is no issue.  And I find this very natural.

Anyway, I think the design of the trait system is very much going towards a "best of both worlds" direction.  You can define methods and you can define standalone functions.  Based on our last meeting, there seems to be general agreement towards a system that allows methods to be imported as standalone functions that take the receiver as first argument as well [1].   Like most modern languages—including Haskell, Java, and C#—we support both parametric polymorphism (bounded generics) and ad-hoc polymorphism (trait types).  Both have their place, after all.

So basically there is no conflict here.  You use the right tool for the job.


Niko

[1] As initially proposed here: https://mail.mozilla.org/pipermail/rust-dev/2012-August/002255.html

October 10, 2012 6:01 PM

That would indeed be pretty unusual. Worse, it's how you write code
normally. If function applications start with the name of the function
and not the variable, then you have to decide before you even start
that you want to invoke the "code completion feature" on this next
_expression_, *skip* writing the function name (or put in some kind of
placholder) and instad write the variable you want to act on combined
with some magic incantation to make the IDE let you select and then
pre-inject the function name in front of it. In an OOP style syntax
you just write code like you're used to and the IDE just discretely
flashes up little tool tips and lists of members to gently nudge you
along your way (or allow you to short cut some typing) as you go about
your normal coding. You don't need to do anything special, it's just
constantly there if you need it, and can be ignored if you don't.

I really can't stress enough what a force multipler it is to write
code in an IDE with powerful code completion for someone who has
internalized it into their workflow. I know real men use VIM etc., but
I'm secure enough to admit that I am more productive in the actual
coding phase using C# with Intellisense in VS than Haskell + Emacs or
whatever, even though I recognize that on language merit alone it
should be the other way around by far. Tooling matters so much these
days. Arguably more than a lot of language semantics, and certainly
more than worrying about slight syntactic redundancy, IMO.

October 10, 2012 3:49 PM

2012/10/9 Lindsey Kuper <[email protected]>
> And, in case nobody has pointed them out yet, here are a couple more resources:
>  * An introduction to traits on Patrick's blog:
>  * A talk I did in August, with a side-by-side Haskell/Rust
> Cheers,

Thank you for the references Lindsey.

2012/10/9 Joe Groff <[email protected]>
> One minor non-semantic, non-scientific benefit of OOP syntax over
> Algol-style function application syntax is that it's syntactically
> more composable—compare `a.foo().bar().bas()`, which reads
> left-to-right and composes to the right, to `bas(bar(foo(a)))`, which
> reads inside-out and requires bracketing on both sides of an inner
> experssion to compose calls. (Of course this only works if your APIs
> are designed up front in a "fluent" way, and isn't quite as flexible
> as what you can do with combinators in ML or Haskell.)

I'm a math guy so to me syntax `bas(bar(foo(a)))` is natural. Besides as you noticed yourself this only work in certain circumstances. For example when you have additional parameters, the syntax could sometime be something like `c.bar(a.foo(b)).bas()` which is less readable in my opinion than `bas(bar(c, foo(a, b)))`. But all this is very subjective, isn't it ?

2012/10/9 Sebastian Sylvan <[email protected]>
> I think there are two other benefits. The first is intellisense/code
> completion. Type "foo." and get a list of all methods in scope with a
> matching self type. This can be a massive productivity boost as it
> basically puts all your code and documentation in the IDE in a context
> sensitive way. Much harder to do this if you have to remember the name
> of the method before you see anything.

I agree that it's easier to start from a variable existing in the context rather than from a namespace or from a function name. But completion does not necessarily have to insert code after the considered identifier, even if it is indeed more usual.

> Second, and much more subjectively, I think there's a difference
> between sort of value oriented programming, and the more low level
> mutable programming type where you're dealing with "objects" that have
> identity and complicated invariants. Subjective, but tying the methods
> to the data type in those cases "feels" okay to me because they kinda
> are inseparable in practice, so they might as well be syntactically
> tied together as well (with a more functional style you would group
> them by modules and visibility, but syntactically it doesn't look like
> an indivisible "object" that have a small set hard-wired built in
> methods on it as opposed to a value and a random grab bag of
> operations that happen to have matching types but could be written by
> any old schmuck and not necessarily "part of the object").

This is precisely the subjective feeling I don't agree with. This feeling only exists because you think of processes/functions/methods as part of a single object rather than interactions between a group of objects. This is a very "Aristotelician" view. It sometimes work well (when object can be seen like "active" autonomous entities) but not in the general case IMHO.

2012/10/9 Nathan <[email protected]>
> It does not make sense.  Think about a general add interface.  It's
> type could be like the following (I am still fuzzy on rust's memory
> location/lifetime type modifiers, so here I assume T is somehow
> copy-by-value):
> add( a: T, b: T ) -> T
> There's nothing special about a compared to b, and also the return
> type is the same type.  If we could generalize this to an interface
> called "Addable", then a single implementation would exist for a given
> type, and there'd be nothing special about which argument position(s)
> have that type.

Hem ... So this mean we agree right ? My point was precisely that this does not make sense. The syntax need not to give one of the arguments a specific role.

> The common OO idiom you allude to specifically ties interface, data
> accessibility, and namespace together.  That's a particular choice.
> You are right that these can be decoupled.
> I'm personally a fan of "fewer ways to do it; less flexibility", so I
> like nailing down a namespace and data accessibility for example.  Or
> for another example, I'm a fan of "the namespace must correlate
> directly to the file path in some manner" versus having those
> decoupled.

I may only partly agree. I too prefer Bertrand Meyer's "A programming language should provide one good way of performing any operation of interest; it should avoid providing two." to Larry Wall's "There's more than one way to do it". But this only apply to perfectly equivalent contructs. If there is several way to do related but distinct things, I found flexibility better.

> But that's just me, and I see more flexibility in rust.  I'm not
> certain but I think data accessibility may be tied to namespaces
> (which are decoupled from filenames somewhat), as well as types, so
> maybe it already has the flexibility you describe?

In all the docs I've read until now, the word "private" only appeared in the section for classes, so I guess no.

> This is intriguing, but not as general as multi-parameter type
> classes.  Consider an interface for two types, A and B, like this:
>
> insert( elem: A, container: B ) -> void;
> peek( container: B ) -> Option<A>;
> There's no (A,B) parameter or return type anywhere.  That would fit
> into insert(), but it would not make sense for peek, where I have no B
> at hand.

This is definite the kind of interface/trait I would like to be express in Rust. But apparently it is not possible with the current design.

2012/10/9 Bennie Kloosteman <[email protected]>
> Multiple parameter are very useful eg a collection and a second type ,
> but it gets messy... it works ok provided its in a single compilation
> unit but else you get compilation unit dependency loops ,with generics
> ( which are similar) you can  auto generate new  libs to handle this
> not sure about rust traits..

Hi Ben. I'm not sure to understand the link with compilation loop. Any previous discussion on this topic?

So in conclusion, this means multiple parameter traits are not planned to be implemented in Rust ? right ?

--
Eddy Cizeron
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev
October 6, 2012 11:14 AM

Hello,

I'm new to Rust, and I have some questions concerning some aspects of the language. From what I understand until now, rust has no dynamic  linkage for methods as it would be the case in any standard OOP language. This is surely an intentionaly different philosophy from the current trend among other recent programming languages. But why not after all. I like the idea. So I find myself skeptical when I read the chapters of the documentation about OOP-like concepts like classes or interfaces.

The essence of the "class" concept lies in the possibility to gather a data structure (a type) and the related processes that can act on the corresponding instances (methods) in a single bundle. But, tell me if you disagree, the only interesting purpose of such an operation is the ability to use inheritance. If no concept of inheritance exists, I hardly see what is the need of making functions "pets" for data. All we obtain are classical design issues like "is my 'f(a,b)' function a 'a.f(b)' method or a 'b.f(a)' method ?" (and I don't think the rust "resource" concept requires classes, as destructors or destructor-like functions are not methods). I'm afraid that the answer is merely to offer a supposedly "attractive" syntax for OOP users. And I think this would be a weak argument

The concept of interface/implementation in rust is surely different than the usual one, but again to me it looks like it has some limitations due to the same reason than above: it tries to look OOP. I could have imagined a very similar interface concept but that does not focus on a single type:

iface shape_drawing<Sf, Sh> {
    fn draw(Sf, Sh);
    fn bounding_box(Sh) -> bounding_box;
}

fn draw_twice<Sf, Sh with shape_drawing<Sf, Sh>>(surface: Sf, shape: Sh) {
    draw(surface, shape);
    draw(surface, shape);
}

Of course this example is a simple one. And any consistent system like this (if it does exist but I'm pretty sure it does) would have been more complex than the current one. But also more general and then more powerful.

Let me clear about something: I don't expect anybody to change the current design. I'm nobody and I know it's always easier to criticize than to do things. I'm just expressing the feeling that while Rust tried to take a different direction from all the other new JVM-compliant-OOP-languages-with-functional-features (which I really really value very much) it seems it has not got rid of some conceptual patterns that are not, IMHO, necessary anymore. And I wish people could give their opinion about this.

Thank you

--
Eddy Cizeron
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev
_______________________________________________
Rust-dev mailing list
[email protected]
https://mail.mozilla.org/listinfo/rust-dev

Reply via email to