Feature request that'll never happen: Traits

https://tracker.xojo.com/xojoinc/xojo/-/issues/74262

Hey, I’m realistic… Xojo as a language doesn’t grow any more. Still, I figured I’d put the feature request in so it can collect cobwebs.

So what are traits? Well, read the request. But the high-level concept is an interface with properties, completed methods, and so on. They aren’t interfaces, so just adding these things to interfaces isn’t the answer, though traits and interfaces are similar. There’s a reason other languages have implemented both interfaces and traits.

Here’s my real real use case, rather than using vague examples:

Class Super
Beacon.ServerProfile
Ark.ServerProfile Beacon.ServerProfile
Ark.NitradoServerProfile Ark.ServerProfile
SDTD.ServerProfile Beacon.ServerProfile
SDTD.NitradoServerProfile SDTD.ServerProfile

The two NitradoServerProfile classes have a lot in common. I could add a Beacon.NitradoServerProfile interface, but that doesn’t really help much because both classes will still need to duplicate their implementations. Extension methods don’t help, because I need to access private class members. This problem is easily solved in other languages using traits, but in Xojo the only solutions are code duplication or maybe some kind of “implementation” class that both subclasses could have an instance of. Neither solution is particularly fantastic.

And while we’re talking about similar classes, how about some attention on https://tracker.xojo.com/xojoinc/xojo/-/issues/73635? Keeping track of these classes in Xojo’s tabs can be pretty annoying.

2 Likes

There are many such features missing in Xojo. Having advanced features is sometimes necessary, probably even useful for the IDE itsef. Never heard of traits, since i never needed such. What i woul like is properties to class interfaces. I’ll try enlighten myself about traits now, seems interesting… voted it up since it’s probably useful.

This does summarize what you need?

A Trait is a group of properties and methods for code re-use, and multiple can be added to a single class. Good for organization and reducing repetition. An Interface is a set of method signatures to enforce a particular implementation in the class they’re added to. Good for adding structure and standardization.

That sounds really useful.

1 Like

I like PHP’s description:

Traits are a mechanism for code reuse in single inheritance languages such as PHP. A Trait is intended to reduce some limitations of single inheritance by enabling a developer to reuse sets of methods freely in several independent classes living in different class hierarchies. The semantics of the combination of Traits and classes is defined in a way which reduces complexity, and avoids the typical problems associated with multiple inheritance and Mixins.

A Trait is similar to a class, but only intended to group functionality in a fine-grained and consistent way. It is not possible to instantiate a Trait on its own. It is an addition to traditional inheritance and enables horizontal composition of behavior; that is, the application of class members without requiring inheritance.

1 Like

I agree that it would be good if Interfaces had properties. I would also like them to have event definitions. It would allow an interface to automatically define the events that that interface would support:

Allow Class Interfaces to define Event Definitions as well as methods
https://tracker.xojo.com/xojoinc/xojo/-/issues/64017

Can you explain how adding code to an interface method, within the Interface editor, would differ from a trait? Assuming they would also be able to have properties and event definitions?

I think you’re missing the point. Interfaces are contracts. They don’t have logic of their own. What good are properties and events without logic? Traits are what you’re looking for in this context. Again, other languages have not made the mistake of mixing the two concepts, and I don’t think Xojo should either.

One of the key differences with a trait is being able to access overridden methods. If the implementor has a method that matches the trait, the implementor gets priority. If you want to access the trait’s version of the method, casting would be used. Since an interface is just a contract, the interface doesn’t have an implementation that can be called. Unless you’re talking about also allowing interfaces to have method implementations, which I think is a bad idea.

1 Like

That was exactly what I was asking, what is the difference between a Trait and an Interface with method implementations? I can’t think of a difference. Please explain what the difference, other than the name, would be and why you think it’s a bad idea. I’m not arguing for or against it, I’m just interested in the thinking. Traits, as you define them, sound useful but I can’t see the difference.

They’re definitely similar and I fully understand the desire to merge the two concepts together. And to be honest, I don’t have a great explanation for why they aren’t the same concept. You can read The difference between Traits, Interfaces, and Abstract Classes in PHP - Andrew Schmelyun to better understand the differences, but that doesn’t really explain why they should be separate concepts. I do know that every (or maybe most) other languages that have the concepts, keep the two separate. Essentially, people smarter than I have decided that merging the concepts is a bad idea, and I’m not going to pretend to know better.

There’s also conversation at > Traits are interfaces So why not use "interface" keyword? | Hacker News which basically just goes around in circles. The wikipedia article at Trait (computer programming) - Wikipedia describes traits as a way to extend the functionality of a class, and we know that interfaces are merely contracts that tell a class things they are responsible for doing.

I think that a huge differential would be association of Traits to classes OR structures, so structures could act like a kind of smart structured “memoryblock” (with unions, different structured views of the same block), and both classes and structs would get methods definitions AND default implementations of those methods, and constants, and enums. All those belonging to a Trait. An Interface does not carry so much functionality, just the methods definitions needing implementation where they are applied.

Nothing in those links seem to give any reason for them to be different.

In terms of Interfaces not having enums and constants, I can’t see why they can’t have, or even shouldn’t have them, even as they currently exist it would be a useful addition. I already wish they had event definitions.

As I said I’m not bothered if they are Traits or an extended version of Interfaces. It does sound like a good idea that I would be happy to support.

An interface just says “you must have these methods” a trait is different since it allows class like behavior. An interface does not.

2 Likes

They’d have to make the IDE treatment of Interfaces much more robust. Currently, there is a tenuous relationship between your Class and an Interface. The IDE sometimes adds the empty methods for you, but not always, depending on how and when the interface is created. You don’t know there’s a problem until you try to compile. Also, what should happen when you remove the trait from the class? Do you wind up with code that now refers to properties that no longer exist?

While that could be allowed, it’d be a bad design. A property defined in a trait should only be referenced by the trait’s code. But yes, if you referenced a trait’s property in an implementor, then removed the trait, you’d have a compile error because the property is no longer there.

Great request! I could have used traits in more than one instance, and I see it is a much better solution than multiple inheritance.
Of course they must be separated from interfaces: interfaces are abstract definitions of methods whose implementation changes according to the implementor, traits are “pieces” of shared implementations.

2 Likes

Traits are set of features you can assign to data. This remembers a class with methods, but disjointed, you can start with just the data and add lots of different traits for such data after. Traits allows signature collisions but must provide composition and disambiguation modifiers in the language to allow the users to pick exactly what they want (scope rules, aliases, name exclusions, etc) to avoid a collision error condition. This allows devs to create structures (also classes without methods) and assign traits (e.g. methods) to them, so such “block of data” knows statically at compile time how to handle its contents, and unnecessary code (not used methods from a shared trait) can be detected and stripped off by the compiler ending with smaller apps.

Traits are great, but implementing it in a way to be proud of, demands lots of design efforts from Xojo that as Thom said “may never happen”.

from a example i saw in web traits looks like an include “file”.

class
… own methods
… traits methods

similar to call any module method from within a interface method or standard method to reuse code instead of copy paste.

They are not at all similar to an include.

i mean
original


<?php
trait HelloWorld {
    public function sayHello() {
        echo 'Hello World!';
    }
}

class TheWorldIsNotEnough {
    use HelloWorld;
    public function sayHello() {
        echo 'Hello Universe!';
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHello();
?>

looks similar

<?php
trait HelloWorld {
}

class TheWorldIsNotEnough {
included
    public function sayHello() {
        echo 'Hello World!';
    }

    override public function sayHello() {
        echo 'Hello Universe!';
    }
}

$o = new TheWorldIsNotEnough();
$o->sayHello();
?>

https://www.php.net/manual/en/language.oop5.traits.php

I… suppose.

Your example is a simple bad designed example using from ONE language.
And “Using” a Trait is not the same as “Including” a source, there’s much more.

Even reading your real example is painful using include (That you hid how it looks in your example)

<?php

class TheWorldIsNotEnough {

    include 'TheWorldIsNotEnough_sayHello.php'; // you have no idea what's that without opening that file

    override public function sayHello() {
        echo 'Hello Universe!';
    }

}

$o = new TheWorldIsNotEnough();
$o->sayHello();
?>

Traits allows compositions resolving conflicts. That’s much more powerful than just overriding. And can’t be emulated using includes. Take a look at the same PHP page you pointed out:

<?php
trait A {
    public function smallTalk() {
        echo 'a';
    }
    public function bigTalk() {
        echo 'A';
    }
}

trait B {
    public function smallTalk() {
        echo 'b';
    }
    public function bigTalk() {
        echo 'B';
    }
}

class Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
    }
}

class Aliased_Talker {
    use A, B {
        B::smallTalk insteadof A;
        A::bigTalk insteadof B;
        B::bigTalk as talk;
    }
}
?>