One of the features of this framework is the ability to declare the “what” using subject-verb-object pattern. So I recently set off on a search to see if this already exists in the Perl ecosystem. I found what I was looking for right in the official Perl docs. Method Call Variations
my $obj = Tree->new();
$obj->Dog::bark();
The docs come with this warning:
“This calls the bark method from class Dog on an object of class Tree, even if the two classes are completely unrelated. Use this with great care.”
Which in my opinion should be removed from the docs and this paradigm promoted as a language feature. Also, Dog does not have to be a class in the OO sense but just a package, so package Dog can contain non OO functions.
The framework also has some keywords, “ASSERT, FAILURE, ALWAYS”. Couple the above with Try Catch to get a similar effect. Giving you code that looks like this:
try {
$character->Location::is_in($location);
$character->Wallet::contains($credits);
$character->Wallet::pay($amount)
} catch {
…handle error, rollback credit deductions etc…
} finally {
print $character->credits;
};
Steps can contain other steps, so the Wallet::pay could be a combination of:
$character->Wallet::debit($credits);
$other->Wallet::credit($credits);
Add in your own messaging service to the function classes and a stash or cache system to carry intermediate values through steps; and you are well on your way. Programming like this does require greater discipline. Keeping functions discrete and objects small. Your objects should be little more than smart structs / dataclasses. All computation is done in functions, something a fellow developer called “object shell, functional core”. One of the metrics for if a function was discrete enough turned out to be that most functions did not contain more than about five lines of unique expressions. Once you took out get the args and the “outcome” return. This makes for very easy testing, reording of steps and easily assembled aggregate functions.
And that’s it, declarative programming in perl with SVO (Subject-Verb-Object) pattern. Here is a quickly hacked together - incomplete but working - example:
]]>First, I love Mojolicious and it's ecosystem, have used it at the 2.x and 6.x levels and hoping to use it on an upcoming project.
Thanks. This is an interesting use of roles, this is "shared behavior" - just shared out to lots of different apps. Also, looking at the Test Mojo Roles on CPAN, the end developer does not "with" the roles and thereby consume them on their code directly, but tells Test Mojo WithRoles what roles it wants ( on the use line ) then gives you a "new" Test Mojo instance with those roles applied at runtime, almost like a factory. If any of those roles methods was called on another instance $t before my $t = Test::Mojo::WithRoles->new('MyApp'); the call would fail. Also, if you don't read the "importing" section clearly and just the synopsis, you may be surprised by this behavior. This is clever, I see why it works here, I see how it creates an almost plugin like ecosystem and I am not necessarily against it but, I would not want it to be a pattern in app dev code. This constitutes a pseudo inheritance mechanism and could be confusing to new devs cause you use "new" but there isn't any constructor composition where you can add roles to an instance at construction. That has to be done through "use" and multiple "uses" of the same module, in a file, are not a common pattern. I'm interested, does this break obviously when two roles have method name collisions? I think you would get the breakage at compile time if you "with" these roles, but here you won't get it till runtime, hmmm... yes, you've convinced me, not a patten I want in app code.
While researching, I saw lots of great examples of roles on CPAN and examples of roles being used in CPAN modules. The examples I give are in the wild where OO syntax has been used but with woeful disregard to good OO practices and design in general, and this is just one more atrocity on the stack.
]]>One of the major benefits of roles is they attempt to solve the diamond problem encountered in multi-inheritance by requiring developers to resolve name collisions manually that arise in multi-inheritance. Don't be fooled however, roles are a form of multi-inheritance.
I often see roles being used in ways they shouldn’t be. Let’s look at the mis-use of roles, then see an example of shared behavior.
I’m using that word inheritance a lot for a reason, one of the two ways I see roles most often misused is to hide an inheritance nightmare.
"Look ma, no multi-inheritance support, no problem. I’ll just throw stuff in roles and glum them on wherever I really want to use inheritance. It all sounds fancy, but I am just lumping stuff into a class cause I don’t really understand OO principals."
One tenet of good object oriented design (GOOD) and not just OOPS ( object oriented programming syntax ) is Composition over Inheritance. All too often roles get used where composing an object, possibly with method forwarding, or an external function, is really what you want. How do I know? Because there is no use of the wrappee self in the ‘role’. Roles are a form of delegation ( this term is mis-used for method forwarding in some documentation ). Delegation is analogis to inheritance, and in delegation the *self* in the wrappee role is the consuming class’ (wrapper) self. Once execution is handed to a role method, all work is done inside role sub routines till either a side effect finishes or a result is generated then execution returns to the code in the wrapper class. Yet, in the class consuming the role, “self” is never called or not called in a way that is meaningful to the class.
This brings me to the next mis-use; roles for code organization. If there is a one to one relationship between the role and it’s use, then this is nothing more than code organization (and obfuscation), stuffing code under the bed like an adolescent boy who told to clean his room, doesn't make it clean. Roles being used as a way to break a 5000 line file into five 1000 line files. Why would someone do this? Because in their heart they know that they have that class doing way too much - it smells. Not understanding good OO, the class grew into a god object. Knowing this is wrong but either not knowing why or just not caring, code was broken into roles along some perceived associations. As other developers came along, prior art was followed and possible encouraged. Unfortunately, as functionality was further glummed on, things just got thrown into the most convenient role or the use of roles exploded as a way of expanding class functionality but pretending like it was being done in a good OO way. There is no shared behavior, the roles are consumed in exactly one class. How to get out of this is a topic for another article. For now, the code from the role should be merged into the same file as the class so you can really see what you have and go from there.
Another version of the one to one is when roles are used to define an interface, either with some function bodies or not, but mostly just “requires
So what are roles good for? In Perl roles are analogies to traits. They can define behavior with full methods built out and or require the composing class implement methods to parameterize the behavior.
So what does shared behavior look like? Here's a contrived example. Please don't do this, there are better ways to handle this sort of thing, I'm just showing shared behavior, not the correct way to handle THIS problem.
package Comparable;
use Role::Tiny;
requires 'area';
requires 'perimeter';
sub can_contain {
my ($self, $other) = @_;
my $can_contain = $self->area >= $other->area;
return $can_contain;
}
sub is_bigger {
my ($self, $other) = @_;
my $is_bigger = $self->perimeter > $other->perimeter;
}
#######################################
package Square;
use Role::Tiny::With;
with 'Comparable';
sub area {}
sub perimeter {}
package Circle;
use Role::Tiny::With;
with 'Comparable';
sub area {}
sub perimeter {}
#######################################
# ... somewhere else...
$square->can_contain($circle);
$circle->is_bigger_than($square);
Do you have any great examples of roles being used correctly?
As I researched this article, I discovered I don't think roles really fit with my view of objects in general. That is, they are often over used in ways that would be better suited by dependency injection, or external functions.
Peace.
]]>But it looks like authors chose to go the Net::RRD::Protocol and Net::BGP::Transport route, and other variations, instead of Net::Protocol::Application::RRD and Net::Protocol::Transport::BGP naming which would A) Make sense and B) Be guessable when looking for something.
]]>There are some limitations - you have to let mongo generate your _id - and the PUT /:db/:collection/:id route doesn't use the id portion from the route; it's this way to work with the JS framework generated URLs I am working with, and some of the code could be abstracted more. However, all in all, this should be a good base for anyone wanting to start with mongo but not touch the shell UI to get a db up and going. The other use is a quick, 'no backend' style setup where front end devs may not know their data structure and just want a data conduit to throw ajaxy requests at. While creating this, I had three lines that setup a Test::mongod instance and fed the port to MongoDB::Client. Like that, you just install the mongod libs, cpanm Test::mongod, launch and tell your frontend devs where to point their REST client - done.
This was so trivial to write up, I don't see why Perl couldn't have several options in whatever your flavor of the day framework is - that is our way right?
]]>use Test::mongod;
then....
my $mongod = Test::mongod->new; # get a temp db on a random port.
my $port = $mongod->port; # get the port to feed to your client app.
Be aware that depending on your hardware, this will block till the server is listening and ready to receive queries - which could be instant or take several seconds. To monitor startup get the db path and then tail the log file in another terminal.
diag $mongo->dbpath
Now to make some sort of fixture loader. Hmmm... what should it be call?
]]>