http://www.perlmonks.org?node_id=953898


in reply to Re: Idea: Moose Mutal Coercion
in thread Idea: Moose Mutual Coercion

OK, so perhaps first and last names wasn't the best way to describe my idea. I agree with all these points about the difficulties of storing names but there's no need to lament about users who don't exist! It was just an example.

The idea is: A formal way of describing the relationship between attributes that can constructed from each other. Lets start again. After thinking about the problem some more I'm going to rename this from "mutal coercions" to "invertible builders".

Here's a different example that is hopefully less confusing:
package Equation_Problem; use Moose; use MooseX::Invertible_Build; has 'a' => ( isa => Num); has 'b' => ( isa => Num); has 'c' => ( isa => Num, invertible_build => { #declare which attrs are part of #our invertible builder group #and the invertible function to base #their builders off of A => 'a', B => 'b', via => 'sum(A, B)', }, ); package main; $math_problem = new Equation_Problem( a => 4, b => 5); print $math_problem->c # prints 9 $math_problem = new Equation_Problem(c => 7, b => 4); print $math_problem->a #prints 3

So what is MooseX::Invertible_Build doing?
It defines this function sum() which can be used in a string as part of the 'via' value in the invertible_build option. sum adds two numbers together and returns the result, so in this case it would add the 'a' and 'b' attributes together and return the result.

But aha! this function has an simple inverse. The inverse of the sum of 'a' and 'b' with respect to a is the subtraction of b from c. And so behind the scenes MooseX::Invertible_Build creates not only a builder for c which in this case is something like:
default => sub{$_[0]->a + $_[0]->b}
but it also creates a builder for 'a' and 'b' which for 'a' would look like:
default => sub{$_[0]->c - $_[0]->b}
Now that my idea is more clear lets return to the original example which involves strings and change it a little (and ignore the complexities behind generalizing human names).
has 'full_name' => ( isa => 'full_name', coerce => 1, lazy => 1, invertible_build => ( A => 'first_name', B => 'last_name', via => 'join(' ', A, B)', }, );

now our 'via' statement contains the 'join'function....and Once again it has an inverse! the inverse of join(A,B) with respect to A is (split(' ', C))[0] (with C being full_name). Not only do we know what the builder for full_name is but we also know it for first_name and last_name. MooseX::Invertible_Build goes ahead and defines the default methods for first_name and last name with their respective split statements.

So right now concrete idea for MooseX::Invertible_Build is:
  • a library that contains a number of functions and their inverses that may form part of an expression to describe the relationship between attributes
  • some sugar to associate multiple attributes with the same expression
  • A parser that can parse these expressions and set the appropriate builder methods on all attributes involved

  • Thanks for taking the time to read all this monks. I'll try and get something working this weekend. Any comments/ideas/advice on where to start welcome. Also a description of the mathematical concepts involved would be cool. I used the word 'inverse' a lot which I think is correct, but I forgot all the mathemical jargon to do with sets, functions and mappings etc.

    Replies are listed 'Best First'.
    Re^3: Idea: Moose Mutal Coercion
    by tobyink (Canon) on Feb 16, 2012 at 08:36 UTC

      A problem I see with this is there are two types of builder this could be used for:

      • Simple ones. For example, inverting the following builder:
        sub _build_net_income { my $self = shift; return $self->gross_income - $self->taxes; }
        to automatically create _build_gross_income and _build_taxes.

      • Complex ones. For example, inverting the following:
        sub _build_net_income { my $self = shift; my $gross = $self->gross_income; my $income_tax = do { if ($gross < 6_475) { 0 } elsif ($gross < 43_875) { (0.2 * ($gross - 6_475)) } elsif ($gross < 150_000) { (0.2 * (43_875 - 6_475)) + (0.4 * ($gross - 43_875)) } else { (0.2 * (43_875 - 6_475)) + (0.4 * (150_000 - 43_875)) + (0.5 * ($gross - 150_000)) } }; my $child_tax_credit = 545 * scalar @{$self->children}; my $taxes = $income_tax - $child_tax_credit; $taxes = 0 if $taxes < 0; return $gross - $taxes; }
        to automatically create _build_gross_income and _build_children.

      In the case of complex builders, I don't think your module has much chance of success. In the case of simple ones, you're not saving me much work.

    Re^3: Idea: Moose Mutal Coercion
    by BrowserUk (Patriarch) on Feb 15, 2012 at 11:03 UTC

      This is no criticism of the goals of your module.

      But ... you knew it was coming right? ... Do you have a more realistic example of how and why it might be used?

      The problem with using a toy example to illustrate the functionality, is that it ends up looking like a solution looking for a problem.


      With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.

      The start of some sanity?

        A less-distracting example might be a ball with mass, velocity, and momentum (calculate the third given two). I still think reaching for a lazy-building pile of Moose goop is wrong, since it's way easier to just have a cache:
        sub momentum { my $self = shift; $self->{momentum} //= $self->{mass} * $self->{velocity}; }
        but at least this may let people focus on the idea.

          You think that the OP is suggesting that Moose herders use his module so that they can type this:

          package Equation_Problem; use Moose; use MooseX::Invertible_Build; has mass => ( isa => Num); has velocity => ( isa => Num); has momentum => ( isa => Num, invertible_build => { A => 'mass', B => 'velocity', via => 'A * B', }, );

          To 'save' typing:

          package Equation_Problem; use Moose; has mass => ( isa => Num); has velocity => ( isa => Num); sub momentum { my $self = shift; $self->mass * $self->velocity; }

          Hm. It can't be that in(s)ane.


          With the rise and rise of 'Social' network sites: 'Computers are making people easier to use everyday'
          Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
          "Science is about questioning the status quo. Questioning authority".
          In the absence of evidence, opinion is indistinguishable from prejudice.

          The start of some sanity?