The Artima Developer Community
Sponsored Link

Weblogs Forum
Eureka: Forget Interfaces what I really need are Traits!

21 replies on 2 pages. Most recent reply: Jan 8, 2006 11:38 PM by Howard Lovatt

Welcome Guest
  Sign In

Go back to the topic listing  Back to Topic List Click to reply to this topic  Reply to this Topic Click to search messages in this forum  Search Forum Click for a threaded view of the topic  Threaded View   
Previous Topic   Next Topic
Flat View: This topic has 21 replies on 2 pages [ « | 1 2 ]
Kresimir Cosic

Posts: 34
Nickname: kreso1
Registered: Jan, 2006

Re: Eureka: Forget Interfaces what I really need are Traits! Posted: Jan 5, 2006 11:51 PM
Reply to this message Reply
Advertisement
Ok… now I basically agree with you, with some subtle differences. But what happened is that we are discussing wrong things. Because my basic complaint against traits was the use of structural subtyping ("requires" keyword). If traits were to use nominal subtyping, then I have very little against traits. And it looks to me that it is really easy to add nominal subtyping to traits, but then they look like multiple inheritance.

Another thing is that I was ambiguous with the term "multiple inheritance", because I don’t limit it to multiple inheritance as implemented in C++, but I think it should implement many other features. Especially some nice features that traits have.

The less important thing is that, when not considering nominal/structural subtyping, I still like multiple inheritance a bit more than traits because of added flexibility. I repeat, this is not very important. For example, multiple inheritance allows me to inherit implementations (including fields) from two classes, which might be handy.

What I would like most is the following (multiple inheritance with features from traits):
a) Multiple inheritance, with support for renaming to access any of the supers.
b) Compiler should force an error if it is ambiguous as to which method to call.
c) It should be obligatory to use keyword "accessor" to mark functions that directly access fields.
d) It should be possible to use "exclude_implementation" keyword in order not to inherit implementation. In this case inheritance inherits interface only, and also specifies subtype relation. To be more precise, implementations of virtual (non-final) functions and of "accessor" functions are not inherited, but final functions are inherited.
e) It should be possible to use "exclude_fields" keyword in order not to inherit fields. In this case only fields and "accessor" functions are not inherited.

> 3. If you exclude the whole of the class in a multiple
> dispatch system then you loose the methods and the fields.
> You really only want to resolve conflicts not exclude
> stuff that you want. This brings me to your the next
> point.

Well, it would be nice, but it is impossible to exclude only some features. However, do not underestimate the flexibility of "exclude_implementation" and "exclude_fields". This constructs are more powerful that what traits provide, with traits you can not even have fields, and here you are even getting the option to exclude them or not.

But, I think nobody else but me will ever like my proposal with "exclude_implementation" and "exclude_fields". If that is the case consider the option of removing c), d) and e) and instead introducing the "trait" keyword for classes that do not have fields. This would be a nice mix of multiple inheritance and traits. It's like traits with nominal subtyping.

Kresimir Cosic

Posts: 34
Nickname: kreso1
Registered: Jan, 2006

Re: Eureka: Forget Interfaces what I really need are Traits! Posted: Jan 7, 2006 3:59 AM
Reply to this message Reply
I just thought of a perfect example, and I thought it would be nice to write about it here. I hope nobody minds this monologue I am having.
template <type T:Value>
class Queue
{
private:
	….//implementation
public:
         Push(T element) {…}
         Pop():T {…}
         IsEmpty():bool {…}
};
 
template <type T:Value>
class StackImpl and interface Stack
{
private:
	….//implementation
public:
         Push(T element) {…}
         Pop():T {…}
         IsEmpty():bool {…}
};
 
template <type T:Value>
class DequeueImpl and interface Dequeue :
implements Queue<T> exclude_implementation,
implements Stack<T>
{
manual_rename{
    link Queue<T>:Pop to PopFront;
    link Queue<T>:Push to PushBack;
    link Queue<T>:IsEmpty to IsEmpty;
    link Stack<T>:Pop to PopFront;
    link Stack<T>:Push to PushFront;
    link Stack<T>:IsEmpty to IsEmpty;
    }
private:
	….//implementation
public:
         PushFront(T element) {…}
         PushBack (T element) {…}
         PopFront():T {…}
         PopBack ():T {…}
         IsEmpty():bool {…}
};
Exlanation:
- you can notice bounded generic T:Value . I need to do this to specify that T has default constructor and assignment defined, so Stack and Queue are containers of values instead of references.
- what I want to do with Queue is to specify both interface and implementation at the same place, so that there is no unnecessary duplication of code. If I need only the interface of Queue, I can get it with exclude_implementation keyword. But compiler usually knows whether implementation or interface only is required.
- class Stack provides both interface and implementation of stack, but here I want to give different names to implementation "StackImpl" and interface "Stack".
-the keyword manual_raname in Deque means that I don't want compiler to introduce names from Queue and Stack into Dequeue. But, Deque is still a subtype, and methods from Queue and Stack are present in appropriate namespaces.

What this example demonstrates is how exclude_implementation could be used to avoid unnecessary duplication of code, and how renaming is used to solve conflicting names.

exclude_fields could solve conflicting fields in a similar manner.
I would also like to point out that I know the way how to inherit the implementation of Queue into Deuque (since thier implementations are usually similar) to avoid even more unnesecary coding. But explanation is too long.
- There is only a single tiny problem with this example: I could make the stack at the front or at end of deque. To solve this, again, requires too long discussion.

Any comments would be appreciated, especially criticism.

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Eureka: Forget Interfaces what I really need are Traits! Posted: Jan 8, 2006 3:51 AM
Reply to this message Reply
@Kresimir Cosic

I agree with you that features like "exclude_implementation" and "exclude_fields" would be nice. One way to implement them might be for the compiler to transform the code. E.g.:
class Person {
    private final String name;
    accessor Person( final String name ) { this.name = name; }
    accessor String getName() { return name; }
    accessor String toString() { return "Person: " + name; }
}

Would be replaced by the compiler with something like:
abstract class Person$Trait {
    // accessors for name
    // effectively private because names have $ in them
    public abstract String get$name$accessor();      
    public abstract String set$name$accessor( final String name );
    String getName() { return get$name$accessor(); }
    String toString() { return "Person: " + get$name$accessor(); }
}
 
class Person extends Person$Trait {
    private String Person$name; // delete final
    // accessors for name
    // effectively private because names have $ in them
    public String get$name$accessor() { return Person$name; }      
    public String set$name$accessor( final String name ) {
        Person$name = name;
        return name;
    }
    Person( final String name ) { set$name$accessor( name ); }
}

IE the compiler generates the equivalent of the Trait code. When something inherits from Person, but with the "exclude_fields" clause, it instead inherits from Person$Trait and the compiler adds the appropriate name field and accessors.

Is this what you had in mind?

Kresimir Cosic

Posts: 34
Nickname: kreso1
Registered: Jan, 2006

Re: Eureka: Forget Interfaces what I really need are Traits! Posted: Jan 8, 2006 11:25 PM
Reply to this message Reply
One way to implement "exclude" is code transformation. But there are some details in your last example that I don’t like.

First thing that I noticed is that you mixed up my "exclude" proposal and "properties" (auto-generation of getters and setters). I like properties, but not the way they are in your example. And I don’t think that compiler should automatically generate getters and setters just for the sake of "exclude".

What happened in your example is that programmer of class Person made a mistake by making toString() method an accessor. This forces compiler to exclude implementation of toString(), which is not trait alike.

Also, since I like to be picky, I want to say that it is redundant to mark constructors as accessor, since constructor which isn't is meaningless. And, I always hated C++ and Java's syntax for constructor.

And I don’t think that "accessor" is a good name because it would be mixed up with accessor/mutator terminology, so some other word would be required.

So the example should go
class Person {
    private String name;
    constructor(String name ) { this.name = name; }
    accessor String getName() { return name; }
    String toString() { return "Person: " + getName(); }
}
Which could be transformed to
abstract class Person$Trait {
    String Person$getName();
    String Person$toString () { return "Person: " + Person$getName(); }
}
class Person extends Person$Trait {
    private String name;    
    Person( String name ) { this.name = name; }
    String Person$getName() { return name; }
    
    //and, since no manual_rename is specified,
    //the following two methods must be added also.
    //The best way to add them would be to modify vtable
    //but here I will make it simpler
    String getName() { return Person$getName() }
    String toString() { return Person$toString() }
}


This transformation is required for exclude_fields keyword to work. For exclude_implementation keyword to work, another class Person$Interface would have to be added as superclass of Person$Trait.

And also one important thing that I didn't mention in Deque example is that exclude_implementation allows you to have 3 completely different implementations for Stack, Queue and Deque, which is not possible with traits. It is usually the case that you really need 3 different implementations in such a hierarchy, but Deque example is specific because implementation of Deque can be used as implementation for Queue and Stack without overhead. Meaning that Deque example can be rewritten to use traits (except for the renaming part).

Howard Lovatt

Posts: 321
Nickname: hlovatt
Registered: Mar, 2003

Re: Eureka: Forget Interfaces what I really need are Traits! Posted: Jan 8, 2006 11:38 PM
Reply to this message Reply
2Kresimir Cosic

I think something like you propose is a good idea. It is less typing than a Trait since the Trait is automatically generated by the compiler. Perhaps there is room for an innovative language feature in a new language :)

Flat View: This topic has 21 replies on 2 pages [ « | 1  2 ]
Topic: Are your loops volatile controlled? Previous Topic   Next Topic Topic: Mixins2

Sponsored Links



Google
  Web Artima.com   

Copyright © 1996-2019 Artima, Inc. All Rights Reserved. - Privacy Policy - Terms of Use