In reading this rather long and contentious debate on roles, you might get a a bit confused on a few points. If you read through one of chromatic's rebuttals carefully, you'll see that he listed the following four design goals behind roles:
He then points out that for the latter two cases, you don't want to compose in the behavior of those roles. By consuming a role, you declare that you provide its capabilities, but you provide the implementation. While this is not what we're doing at the BBC, it's a very reasonable use case. However, chromatic then goes on to argue the following:
The moment you throw a mandatory default warning on two of the four appropriate and specified uses of roles, you've penalized them. You're subtly encouraging people not to use the most important and most powerful features of roles! You're actively discouraging people from taking advantage of allomorphism using well-established and long-recommended design techniques explicitly made safer and more understandable by roles.
Had this been said up front rather than buried in a thread, I would have had a much clearer understanding of the issue and much debate could have been avoided. Regrettably, the warning is now getting pulled from roles and people who don't want silent "action at a distance" are now penalized.
So if it's inconvenient to silence those warnings and thus they're argued against, isn't the goal here to go to the root cause of the problem and make it convenient to silence those warnings? In fact, I've sent off a proposal to the Moose list which not only makes it convenient, but proposes an addition to Moose roles which makes chromatic's use both case explicit and easier to use. It silences those warnings but in a safe way.
package My::Customer; use Moose; with 'Role::Serializable' => { includes => [] };
In other words, design goals 1 and 2 would tend to have "excludes" scattered here and there, just as we do here. Design goals 3 and 4 would have "includes" here and there. It would be the mutually exclusive exact opposite of "excludes". If you have "includes => []" for your role, all "non-included" methods would be added to "requires" and you have an interface. No warnings needed in this case. Heck, if you want to treat a role as an interface but still include, say, their "to_xml" method:
package My::Customer; use Moose; with 'Role::Serializable' => { includes => [ 'to_xml' ] };
All methods must be overridden except that one. Simple, explicit, satisfies my needs and seems to satisfy chromatic's. I think it's a good compromise.
The syntax still isn't perfect, but there's only so much you can do with straight Perl. However, I think this illustrates a point which too many of us forget about: when you have a problem, start looking for the root cause and see if you can solve that problem. Coming up with an interim solution which just shoves the problem elsewhere doesn't do anybody any good.
I would prefer to have warnings for possible problems that you would only run into by accident. If you would not want that to happen, you could just declare so.
What's Rolsky's position on the subject, If I may ask?
Re:I think that's a good compromise
Ovid on 2009-04-28T13:28:31
I don't know how Dave feels, but Stevan Little has already said "no". He prefers new features to be tested in a MooseX:: class and that makes sense to me.
Re:I think that's a good compromise
xsawyerx on 2009-04-28T13:47:34
We always return to the same issue though, of whether new users should have to explicitly declare features which make it easier to use, instead of experts explicitly expressing to ignore different warnings.
Moose has become a stepping stone in an Enlightened Perl and any Enlightened programming language, and users should not be bitten by these simple things. Also, users should not (by design), have to know about various MooseX modules to fix the behavior for them.
We should work on making these easy to use for everyone (read: mostly beginner and intermediate users), and then enable more complex features to expert users. "strict" for example, should be on by default and users that prefer to do "no strict 'refs'" could.
Strict isn't on by default because of wrong design much earlier on, Modern::Perl helps fix that.
Of course, these are only my 2 cents, at least right now. I hope I'm not stepping out of line here.
Re:I think that's a good compromise
Ovid on 2009-04-28T14:01:29
You're not stepping out of line at all. I think everyone who has thoughts on these matters should feel free to chip in.
Re:I think that's a good compromise
perigrin on 2009-04-28T16:14:48
The difference, and this is the important one here,
strict
enables warnings about features in Perl that we actively want to discourage, Symbolic references, global variables, undeclared barewords. The Role warning specifically is triggered on valid and more importantly encouraged use cases.There's an idiom of role usage where you define a default implementation of a method and then override that in classes where you need a more specific version of it. You may recognize this, it's one of the reasons people use Multiple Inheritance. Having this use case warn unless you specifically exclude a role penalizes it and subtly implies that it's not a valid idiom.
If that were the end of the story I'd probably say "fork Moose" and be happy to have more competition. But that's not the end of the story.
Perl::Critic
handles cases where it may be against local Best Practice to use what would be otherwise valid and encouraged code. I sort of feel that Ovid is being unfair saying that "people who don't want action at a distance are being penalized" because Shawn Moore has started enabling new critic tests to deal with exactly this case.As for enabling features in the
MooseX::
namespace. This has always been the encouraged method of adding new features to Moose. Things that get vetted inMooseX::
will eventually get folded back intoMoose::
and possibly Moose core. The current best prospects for this isMooseX::Types
,MooseX::AttributeHelpers
, andMooseX::Storage
which are three of the oldestMooseX::
packages out there.There are still two issues that I can think of
xsawyerx on 2009-04-30T13:44:25
First I want to thank you for taking the time/effort to inform me more about this. Sorry for the late response.
Secondly, there are still two issues that make me ponder:
- What about beginners who have their methods overridden without knowing? I'm not saying that warning everyone is the only solution, I just know this is an issue we should think of, because it's more important, IMHO.
- I wish I could say that most beginners don't use Perl::Critic, but unfortunately the case is that most programmers don't use Perl::Critic, not just beginners.
Perhaps this is possible?
use Moose::Role qw(
:override_warn 0 ); Just throwing a belated idea...
Also, a question: when things from MooseX are moved into Moose, are they removed from MooseX or marked as a part of Moose or nothing at all?
Re:There are still two issues that I can think of
Ovid on 2009-04-30T14:46:38
One thing which might help is that I just uploaded MooseX::Role::Strict (it will be a bit before that link is good). Hopefully that will help people who are concerned about this.
Now I'm looking into adding includes into Moose.
Re:There are still two issues that I can think of
xsawyerx on 2009-04-30T15:17:04
Sounds good!
Re:There are still two issues that I can think of
chromatic on 2009-04-30T18:17:13
Perhaps this is possible?
It's possible, but I believe it's inadvisable. It's not the role's business how anyone else wants to perform the role. Any role which dictates how people perform the role (composition, inheritance, delegation, allomorphism) is not a role anymore.
Re:There are still two issues that I can think of
Ovid on 2009-04-30T19:07:28
Sure it's OK. The role isn't telling any class how to perform that role. It's just providing a way of meeting a need that you apparently don't have. Your objection was that by adding the warning, it forced programmers to use clumsy ways of getting around the fact that you might want to use a role as an interface or primarily as an interface. By combining strict roles and 'includes', some of the difficulty goes away. For example:
package My::Class;
use Moose;
# or whatever the final syntax is, if accepted
with 'Some::Role' { includes => [] };That can be a 'strict' role or not, but now it's much easier to say "I want your interface, not your implementation." The role is not dictating how anyone performs it.
With more thought as to how to create an easy to use syntax, we can satisfy both your needs and mine -- though I expect you aren't going to budge an inch on your position, though I have on mine
:) Hmm, maybe as:
package My::Class;
use Moose;
# or whatever the final syntax is, if accepted
inteface 'Some::Role';You can continue to state your point of view. I'll continue to suffer horrible debugging problems because you don't seem to acknowledge my problems are real -- or offer anything even close to a solution to them. So, um, could you at least try to put yourself in my position and think of some way of meeting both your needs and mine? Pretty please, with syntactic sugar on top?
:) Re:There are still two issues that I can think of
chromatic on 2009-04-30T19:54:04
Strict roles reintroduce one of the problems that I always intended roles to solve: enforcing implementation details at the worst possible place, where you know the least about how people will actually use them.
Let me put it this way. I believe that allowing the creator of a role to specify that you must use inheritance or delegation or allomorphism or reimplementation to satisfy the demands of the role is like allowing the creator of a class to say that you cannot inherit from it. The real responsibility for marking a class as (for example)
final
rests with the user of the class, who knows much, much more about how he or she actually uses the class.I'll continue to suffer horrible debugging problems because you don't seem to acknowledge my problems are real -- or offer anything even close to a solution to them.
I've said multiple times that I acknowledge you have a real problem and I've offered what I believe to be the sanest solution for everyone multiple times: explicitly and lexically enable a warning or a composition error for class-local overrides.
That's where you, in specific, know that any overriding is always an error condition. In the absence of very specific coding standards ("All role consumption at the BBC must explicitly exclude the composition of class-local methods"), you cannot know how people will use roles.
Re:There are still two issues that I can think of
chromatic on 2009-04-30T23:17:09
To be fair, there's one category of behavior where it's acceptable for the creator of a role to enforce a usage strategy: where the role only requires the presence of other methods without providing implementations. Note, however, that even that case does not dictate how the composing class provides those methods.
Re:There are still two issues that I can think of
Ovid on 2009-05-01T06:54:46
I apologize. You have offered a solution and just because I'm so frustrated with this discussion, I've mischaracterized your position. I'm sorry about that and I certainly didn't mean to.
Re:I think that's a good compromise
Stevan on 2009-04-28T17:32:02
Actually, I said no to the idea of:
with 'Role::Serializable' => { includes => [] };
meaning "don't compose any methods, but i still want to do this role", because I think that it is not very clear and could very easily be confusing to people. After all you would never say:
with 'Role::Serializable' => { excludes => [] };
it just wouldn't make sense. I just really never liked APIs where the lack of something was significantly meaningful.
Now, all that said, I actually really kind of like:
with 'Role::Serializable' => { includes => [ 'to_xml' ] };
because it is clear and obvious and pretty useful. In fact, if you had proposed this originally, and not tied it to the
includes => []
being a shortcut to a "interface" type role, I would probably have said "go for it".In fact, I would be happy to accept a fork (no more patches now that we are on git) which implemented
includes => [ 'to_xml' ]
(along with tests and docs of course) as long as it didn't do the "interface" role shortcut thing.- Stevan
Regrettably, the warning is now getting pulled from roles and people who don't want silent "action at a distance" are now penalized.
I have to agree with perigrin, this is a rather unfair characterization of the situation. We initially enabled the warning because it is useful and helpful, however the big downside that we all saw was that for people who were well versed in Roles and knew well how they worked were forced to add code to silence the warning. After considerable debate, both in the use.perl thread and IRC, it was decided that this was better solved with Perl::Critic and so Sartak when about doing exactly this. Additionally some work has been done (not sure how final/releasable it is) on making it actually easier for you to write something like MooseX::Roles::Strict, which would be quite painful with the current codebase.
So while of course you are free to disagree with the end decision, please do not short change it. Especially when several people did a fairly non-trivial amount of work to try and make this work best for all parties.
- Stevan
Re:Kind of unfair characterization
perigrin on 2009-04-28T17:23:50
Additionally some work has been done (not sure how final/releasable it is) on making it actually easier for you to write something like MooseX::Roles::Strict, which would be quite painful with the current codebase.
Actually the basic hook you need I think has been released. I went about doing this when this whole discussion started because it *should* have been possible, and Sartak made it work the right way (as opposed to my ugly hack). You can now say:
use Moose::Role -metaclass => 'MyRole::Metaclass'
andMyRole::Metaclass
will replaceMoose::Meta::Role
and override the necessary Role composition bits to compose with your own custom composition rules. This is a lot more work than it should be, but it *is* possible now, as opposed to before when it wasn't even possible without replacingMoose::Role
entirely.