xp-forge / partial

Partials: Compile-time metaprogramming
0 stars 0 forks source link

Even less magic #2

Closed thekid closed 9 years ago

thekid commented 9 years ago

Can we come up with a more explicit way of saying "here, accessor methods as well as equals and toString() are automatically declared":

namespace example;

class Wall extends \lang\Object {
  use \lang\kind\ValueObject»example\Wall;
  use \lang\kind\Comparators»example\Wall;

  private $name, $type, $posts;

  public function __construct(Name $name, Type $type, Posts $posts) {
    $this->name= $name;
    $this->type= $type;
    $this->posts= $posts;
  }
}
thekid commented 9 years ago
use \declares\value\of»example\Wall;

?

thekid commented 9 years ago
use \partial\ValueObject\of»example\Wall;
use \partial\ValueObjectDeclaration\of»example\Wall;
use \partial\declarations\ValueObject\of»example\Wall;

?

thekid commented 9 years ago

I like the partial keyword, as it aligns with partial classes in C#, a concept typically used to combine user-written and generated code.

By rephrasing "here, code is automatically inserted" to "here, we insert (use) a partial declaration", and renaming the namespace to lang\partial, we could achieve:

namespace example;

use lang\partial\declarationof;

class Wall extends \lang\Object {
  use declarationof\ValueObject»example\Wall;
  use declarationof\Comparators»example\Wall;

  private $name, $type, $posts;

  public function __construct(Name $name, Type $type, Posts $posts) {
    $this->name= $name;
    $this->type= $type;
    $this->posts= $posts;
  }
}

Alternatives for declarationof could be:

thekid commented 9 years ago

Another idea is to turn around the order and put the type name upfront. This composes the trait name differently, prefixing the current namespace and ridding us of the necessity of having to repeat it. It also has the advantage of being able to use the keywords as separators themselves instead of the » character

We'd split along the word including in this case:

namespace example;

class Wall extends \lang\Object {
  use Wall\including\ValueObject;
  use Wall\including\Comparators;

  private $name, $type, $posts;

  public function __construct(Name $name, Type $type, Posts $posts) {
    $this->name= $name;
    $this->type= $type;
    $this->posts= $posts;
  }
}

Alternatives for including could be:

We might want to specify what we're using / including:

We might also want to include the namespace to make it extensible (e.g. by com\example\partials classes):

thekid commented 9 years ago

Suggestion

After thinking about this and a bit of prototyping, here's the current idea:

Regular partial

namespace example;

use lang\partial\Identity;

class Name extends \lang\Object {
  use Identity;
}

Parametrized partial

namespace example;

use lang\partial\ValueObject;
use lang\partial\Comparators;

class Wall extends \lang\Object {
  use Wall\including\ValueObject;
  use Wall\including\Comparators;

  private $name, $type, $posts;

  public function __construct(Name $name, Type $type, Posts $posts) {
    $this->name= $name;
    $this->type= $type;
    $this->posts= $posts;
  }
}

Note: Imports actually don't work this way syntactically, that is: The class name of the trait being loaded is example\Wall\including\ValueObject (and doesn't include the lang\partial namespace), and thus the short name ValueObject is the name of the transformation. The resolution takes place by the mirrors library, which parses the imports. As this happens only once it's OK performance-wise. It does, however, ensure the consistency between the above examples!

thekid commented 9 years ago

Closed with 0.4.0 release