Ticket Created
over 3 years ago

WERETECH-12830

RFC: Traits, Roles or Interfaces, a composition API

I'd like to ask the following feature for MonkeyC, in Perl we call them Roles, in Java it is called Interface and Rust calls them Traits. I will refer to them as roles as I'm using Perl on a daily basis and thus is my preferred word for them. Maybe in MonkeyC we can call them bananas, and instead of implementing a banana we call it eat (its a joke btw). The thing I miss the most from the OO system of MonkeyC is a composition API. Roles allows us to not only depend on inheritance but also on composition, which IMO is a very powerful tool. It makes things easier if you don't want to keep extending existing classes and promotes code reuse across your classes. You don't need to extend a class just to get some nice features. Since MonkeyC is ducktyped, it seems that we also need a bigger pond so we can also have the ducks on our classes. Roles, interfaces or traits, whatever we name them, they are fun!


## in resources-fenix6/AppStorage.mc
using Toybox.Application.Storage as st;

(:fenix6)
role AppStorage {
   
   function save(key, value) {
      st.setValue(key, value);
   }
   
   function get(key) {
      var val = st.getValue(key);
      if (val == null) {
         return 'I cannot be found';
      }
      return val;
   }
     
}

##  in resource-fenix5/AppStorage.mc
using Toybox.Application.AppBase as st;

(:fenix5)
role AppStorage {
   
   function save(key, value) {
      st.saveProperty(key, value);
   }
   
   function get(key) {
      var val = st.getProperty(key);
      if (val == null) {
         return 'I cannot be found';
      }
      return val;
   }
     
}

## In your config class

class Config extends DeviceConfig does AppStorage {
  // logic here
}

## But AppStorage isn't always for Configuration

class SomethingNeedsToBeStored does AppStorage {

   function someMethodNeedsStoringData() {
      save("mykey", "Some data");   
   }

}

## Or as proposed with banana's:

banana Bar {

  function foo();
  
}

class Monkey eats Bar {

   function foo() {
      Sys.println("I'm implementing Bar");
   }
}

* Classes can implement more than one role.

In Perl Moo and Moose have access to everything and everyone from the consuming class, and they are writing a new OO system called Cor(inna). With that new OO system they don't think roles should have access to the instance variables. MoneyC knows private/protected and public. I think this makes it easier to shield off things from roles. A point for discussion I think.

* Roles don't have access to private variables of a class

* Roles don't have access to protected variables of a class(?)

* Roles have access to public variables of a class(?)

* Roles should be able to "require" methods for classes that implement them, eg.

role Foo {

  // Perl style
  requires bar;
  // or java style
  function bar();
  
  // This allows us to do this:
  public function foo() {
    var jaja = bar();
    return baz(jaja);
  }
  
  function baz(arg) {
     return true;
  }
  
}

// Now the impementing class can use foo, baz

class Thing does Foo {
  
  // a whole lot of logic here

}

// Somewhere else

var thing = new Thing();
thing.foo()