LabVIEW Idea Exchange

cancel
Showing results for 
Search instead for 
Did you mean: 
Daklu

Traits - A better(?) alternative to Interfaces, Mixins, etc.

Status: Completed

Available in LabVIEW 2020 and later. LabVIEW now includes support for Interfaces.

I've long wished Labview had native support for Interfaces.  Recently I ran across an article describing Traits as a better alternative to Interfaces, Mixins, Multiple Inheritance, etc.  Traits are (as near as I can tell) similar to Interfaces with the main difference being Traits can define a method implementation while Interfaces can only define a signature.

 

I'd like to see Traits implemented in G instead of Interfaces.

 

(Cross posted to LAVA.)

 

 

12 Comments
manu.NET
Active Participant

Hi,

 

Beside the object interface, i also would be interested to have VI interfaces. (VI input/output behaviour description)

This could be usefull to create pluggins applications.

The pluggin behaviour could be described with VI interfaces.

 

Manu.

Manu.net
AristosQueue (NI)
NI Employee (retired)

If you try to add interfaces to LabVIEW, as a thought experiment, you'll pretty quickly find yourself realizing that LabVIEW interfaces want to be more like traits... it just feels more natural. What do I mean by that? VIs come with block diagrams. Every method of an interface would have a block diagram. Why not allow actual code on those diagrams so long as any subVI calls are either to static (non member) VIs (i.e. stuff in vi.lib) or to other members of the interface, which would include data accessors. So I can write an algorithm as part of an interface, as long as any lookups of values call member VIs of the interface. When the class implements the interface, among the functions it has to implement are accessors to data.

 

Depending upon how those default methods get compiled, you can even buy back the performance loss of going through the data acessors... if the interface default VIs are compiled once for all classes implementing the interface, then you get a smaller memory footprint at the cost of having to call the data accessors. But if you compile the interface separately for each class that implements the interface, the code size gets smaller but you can inline those data accessors and you're back to raw Bundle/Unbundle nodes.

Daklu
Active Participant

"the code size gets smaller larger but you can inline those data accessors"

Correct?

 

-------

 

I agree with AQ--traits feel more natural than interfaces in the LV environment.  There are still issues that need to be worked out.  The most obvious one this:

 

When a trait provides (implements) a method it may rely on required methods that are implemented by the class, such as data accessors.  These required methods have to have some sort of representation within the trait itself, independent of the class that uses the trait.  How is that accomplished?

 

I believe AQ is suggesting a mechanic similar to how classes work.  Create trait vis for required methods, define the conpane, and mark them as "required" similar to marking a class method as "must override."  When a class imports a trait I assume the class breaks until the required methods are implemented.

 

That certainly works.  One inconsistency I'm having a little trouble with is the required method has a block diagram, implying it may have an implementation.  That doesn't make any sense to me.  The trait only defines the name and conpane (signature) of the required method.  Ideally it shouldn't need a separate vi just for that. 

 

I wonder if it would be possible to define a vi's conpane in the .lvtrait file?  I'm imagining a special right click option for traits, "Add Required Method."  This pops up a dialog that lets you define a name, conpane, and create an icon, but stores that information in the .lvtrait file instead of creating a new .vi.  The required method shows up as a member of the trait in the project explorer window, allowing you to use it on the block diagram of provided methods.  On the provided method's block diagram the required method would look much like a normal sub vi, possibly with a different border to indicate it is not a "real" sub vi.

 

Another question:  How do you resolve situations where two or more traits are mutually dependent?

 

Trait Waiter{
  Provides TakeFoodOrder;
  Requires TakeDrinkOrder;
  [...]
}

Trait Bartender{
  Provides TakeDrinkOrder;
  Requires TakeFoodOrder;
  [...]
}

Class CustomerServer{
  Includes Waiter;
  Includes Bartender;
  [...]
}

 

Calling CustomerServer.TakeDrinkOrder or CustomerServer.TakeFoodOrder enters an infinite loop.  Classes using traits can "eclipse" (meaning override, but without the parent-child relationship) a trait's provided method by implementing it's own method of the same name, so we could do this:

 

Class CustomerServer{
  Includes Waiter;
  Includes Bartender;

  TakeDrinkOrder(){
    Bartender.TakeDrinkOrder()
  }

  TakeFoodOrder(){
    Waiter.TakeFoodOrder()
  }
}

Unfortunately this violates the flattening property of traits.  The authors explicitly avoided this kind of named references to trait methods so traits could be refactored without breaking any classes that use the traits.  (Later:  Meh, this doesn't break the perpetual loop anyway...)

 

Hmm... I think the answer in this particular case is that my traits are poorly defined--they're trying to do too much.  This is probably a better design. 

 

Trait FoodOrderTaker{
  Provides TakeFoodOrder;
  [...]
}

Trait DrinkOrderTaker{
  Provides TakeDrinkOrder;
  [...]
}

Class CustomerServer{
  Includes FoodOrderTaker;
  Includes DrinkOrderTaker;
  [...]
}

Incompatible traits can't always be resolved this way.  Certainly not if the traits are provided by a third party.  I think it's possible to break the loop by composing a new trait from either Waiter or Bartender, but I haven't thought through that enough.

 

Here are a couple more links about traits:

Traits - Composable Units of Behavior

Traits vs Multiple Inheritance

Applying Traits to the Smalltalk Collection Classes

Traits vs Mixins

 

The last link has an interesting comment copied from Newspeak forums:

 

Traits attempt to address perceived  problem of mixins.

  1. There is very little real experience indicating that these  perceived problems are real. 
  2. Traits are restricted to be stateless. This simplifies matters,  but does not handle all cases of  interest. In fact, there are now  research papers attempting to add  state to traits.

Traits are entirely subsumed by a more  general model, which I devised many  years ago in my PhD thesis (available  off my web site, if you really want to  dig deep).  ...  I would like to examine how we might  incorporate these combinators into  Newspeak. ...

 

Unfortunately the Newspeak forums are down and I haven't been able to research it more.

AristosQueue (NI)
NI Employee (retired)

> "the code size gets smaller larger but you can inline those data accessors"

> Correct?

No, larger was correct. Instead of a single block of assembly code shared by all implementors of the trait, you have a separate version of the assembly code specific to each class.

AristosQueue (NI)
NI Employee (retired)

> That certainly works.  One inconsistency I'm having a little trouble with is the required method has a block

> diagram, implying it may have an implementation.  That doesn't make any sense to me.  The trait only

> defines the name and conpane (signature) of the required method.  Ideally it shouldn't need a separate vi just for that.

 

And it defines the control you get when you do Create Control from a terminal.

And it defies the VI Properties for the Execution page.

And it defines the context help the user sess.

And it defines the base icon of the method.

And it defines the initial inplaceness of the method.

 

We (R&D) had exactly this same conversation when classes were first designed, that it was somewhat common for the base class methods to never actually be invoked. so why have a full VI? But there were enough aspects of defining a method that having a full VI seemed just more complete. It also makes it easier to convert from a method where you don't want to define the diagram to one that does if they're not different types of VIs. 

 

drjdpowell
Trusted Enthusiast
>> Calling CustomerServer.TakeDrinkOrder or CustomerServer.TakeFoodOrder enters an infinite loop.
I don't understand.  That easily resolves to Bartender.TakeDrinkOrder and Waiter.TakeFoodOrder.  There's no loop.
-- James

 

Daklu
Active Participant

 

"But there were enough aspects of defining a method that having a full VI seemed just more complete. It also makes it easier to convert from a method where you don't want to define the diagram to one that does if they're not different types of VIs."

 

Fair enough.  I would hope the block diagram of any required methods would have a clear indication any code present will not be executed.  Perhaps a giant red X as a background watermark or something.

 

 

"I don't understand.  That easily resolves to Bartender.TakeDrinkOrder and Waiter.TakeFoodOrder.  There's no loop."

 

While I didn't explicitly show it by implementing the provided methods, since Bartender provides only a single method (TakeDrinkOrder) it implies that method in turn makes calls to all the required methods (TakeFoodOrder.)  So the call chain goes like this:

 

CustomerServer.TakeFoodOrder            (Delegates to Waiter trait)

-Waiter.TakeFoodOrder                          (Requires TakeDrinkOrder method, provided by Bartender)

--Bartender.TakeDrinkOrder                   (Requires TakeFoodOrder method, provided by Waiter)

---Waiter.TakeFoodOrder

...

 

Remember, a trait is a functional implementation, not an interface to arbitrary functionality.  We don't define methods a trait should do, only what it does do and what it needs from the class to do it.  Required methods are not part of a trait's public api.  They are sub vis used in the provided methods.

 

-------------

 

One thing I don't see addressed in the paper is whether or not classes that all implement a trait can be treated as a single collection.  I assume they can, but I don't know for sure.  It may depend on the language itself.  How do you implement the LV equivalent of this?

 

Trait FoodOrderTaker{
  Provides TakeFoodOrder;
  [...]
}

Class Waiter{
  Includes FoodOrderTaker;
  [...]
}

Class Teenager{
  Includes FoodOrderTaker;
  [...]
}

Class Stewardess{
  Includes FoodOrderTaker;
  [...]
}

main{
  new objArray = [new Waiter, new Teenager, new Stewardess];
  for each item in objArray{
    item.TakeFoodOrder;
  }
}

 

shb
Active Participant
Active Participant

What is the advantage of a trait to an interface plus a utility VI that all implementing classes can call?

Daklu
Active Participant

What is the advantage of a trait to an interface plus a utility VI that all implementing classes can call?

 

It's not clear if the "interface" you're referring to is an informal interface (a way to interact with something) or an Interface construct supported by the language.

 

Informal interface

The advantage of formal Interfaces and Traits over an informal interface is Interfaces and Traits allow you to call the method by referring to the Interface/Trait method, instead of being forced to call it through the class method.  This is very useful when I want to do some operation on an object without having to know exactly what type the object is.

 

Formal Interface

The advantage of Traits over Interfaces is Interfaces (traditionally) are defined as an abstract type and do not include any implementation code.  Labview doesn't support the concept of defining what a type is independently of what the type contains.  You can't declare a string without also defining what the string contains.  You can't declare a vi without having the vi block diagram exist somewhere.  Even abstract classes aren't really abstract, we've simply adopted a convention where we define the methods to do nothing and call it abstract.  In Labview, the type is declared by the act of defining what the type contains.

 

Since the act of formalizing an Interface entails only declaring what it is, and does not allow defining what it does (contains), the concept doesn't work very well in Labview.  Traits, imo, are a more natural fit.
AristosQueue (NI)
NI Employee (retired)

This idea should have been marked as "in beta" months ago, and "in development" before that. My apologies for missing this. We are going to mark this idea as "in beta" now.

 

What is coming in LabVIEW 2020 is not quite traditional interfaces, not quite SCALA traits. It is interfaces with default implementations. But it fulfills the spirit of this particular idea's request that we go beyond just interface declarations. Daklu's summary in his last post ("Since the act of formalizing an Interface entails only...") is dead on and is incorporated into our work.

 

I'm not going to get into all the details here. What my team is building (has built -- we're well into late beta period!) is what I consider to be a good design for a graphical, dataflow programming language, and that means borrowing concepts from several other languages with a couple innovations of our own. It is an effort to balance power with usability.

 

I say all this so that when it comes out, no one monitoring this thread is surprised when they find deviations between some descriptions of traits and what LabVIEW provides. Our aims are the same: type definitions that provide better abstractions than classes. I think we have achieved that goal.