Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a syntax for on-the-fly coercion #68

Open
barefootcoder opened this issue Feb 19, 2013 · 4 comments
Open

Add a syntax for on-the-fly coercion #68

barefootcoder opened this issue Feb 19, 2013 · 4 comments
Labels

Comments

@barefootcoder
Copy link
Contributor

In #65 @schwern proposes a coercion trait. This is good.

However, just as we have both type checking and on-demand typing via where, I think we could benefit from having a way to specify a coercion directly in the signature, which would apply only to that parameter. Let me provide an example of where this could be useful.

I've been putting together some code to deal with simply formatted data in a Google Spreadsheets tab. I want to be able to access the data by column number (or letter) and row number, but I also want to be able to access the data by specifying a column header, or the "key" data of a row. I wrote the following method:

method read_row (Int|Str $rowname)
{
    my $row = $rowname =~ /^\d+$/ ? $rowname : $self->_row($rowname);
    # do stuff with $row
}

where _row is a method that turns a row's key value into the numeric row number. But what I really wanted to write was this:

method read_row ( Int $row from Str via { $self->_row($_) } )
{
    # do stuff with $row
}

That seems cleaner and neater, and should be more obvious to anyone reading my code. The coercion neatly eliminates an entire line of parameter setup code, which is what signatures excel at. (In a way, this is sort of a poor man's multimethod dispatch.)

However, I would never create an entire subtype with coercion just for this single parameter. So on-the-fly coercion seems the rational way to go.

I'm curious to hear if others would find this useful.

@schwern
Copy link
Contributor

schwern commented Feb 20, 2013

See, I would create a RowID type that I can name, test, document and
reuse elsewhere.

My main issues are that it encourages people to cram a lot of detail
into a signature. In the example, it takes up almost the whole line for
a single parameter.

OTOH, I guess there's no reason a signature can't span multiple lines.

OT3H, I believe people are more likely to write out the coercion long
hand (ie. my $row = $rowname =~ /^\d+$/ ? ...) than they are to write
a type with coercion. In that sense, this is a nice compromise. I'd
rather have argument transforms spelled out in the signature.

OT4H, I can't reference the instance object in a regular coercion.
That's pretty cool.

With that in mind, how are you doing to pull off via { $self->_row($_) }?

@barefootcoder
Copy link
Contributor Author

See, I would create a RowID type that I can name, test, document and reuse elsewhere.

But the coercion isn't possible in isolation, because it needs the row-data mapping stored in the object.

OTOH, I guess there's no reason a signature can't span multiple lines.

Oh, sure, I use multiple lines for sigs all the time. For instance,

    method billing_rate_id
    (   
        Company::Company    :$company,
        Company::Property   :$property,
        Str                 :$query_date,
        Str                 :$pick_nm = '0',
        Str                 :$source_tp,
        CodeRef             :$source_picker
    )

which is actual production code.

With that in mind, how are you doing to pull off via { $self->_row($_) }?

'Tis a valid question. I suppose the $_ would have to be subbed out for the given argument and the resulting code injected into the method. That way, the sub could not only refer to $self, but any previous argument. Not sure this is exactly the right approach, but I feel fairly confident there's a way to do it, one way or another.

@schwern
Copy link
Contributor

schwern commented Feb 20, 2013

That seems to me the way to do it. It will require embedding logic to check
if the argument is of the correct type, but that should be a SMOP.
On Feb 20, 2013 8:04 PM, "Buddy Burden" notifications@github.com wrote:

See, I would create a RowID type that I can name, test, document and reuse
elsewhere.

But the coercion isn't possible in isolation, because it needs the
row-data mapping stored in the object.

OTOH, I guess there's no reason a signature can't span multiple lines.

Oh, sure, I use multiple lines for sigs all the time. For instance,

method billing_rate_id
(
    Company::Company    :$company,
    Company::Property   :$property,
    Str                 :$query_date,
    Str                 :$pick_nm = '0',
    Str                 :$source_tp,
    CodeRef             :$source_picker
)

which is actual production code.

With that in mind, how are you doing to pull off via { $self->row($) }?

'Tis a valid question. I suppose the $_ would have to be subbed out for
the given argument and the resulting code injected into the method. That
way, the sub could not only refer to $self, but any previous argument.
Not sure this is exactly the right approach, but I feel fairly confident
there's a way to do it, one way or another.


Reply to this email directly or view it on GitHubhttps://github.com//issues/68#issuecomment-13822081.

@barefootcoder
Copy link
Contributor Author

It will require embedding logic to check if the argument is of the correct type, but that should be a SMOP.

Yes. I'm thinking something like the following. Currently our injected code looks something like so (pseudocode):

type_check('type', $arg, '$arg');
...
sub type_check { ($type, $value, $name) = @_; if (not $type->check($value)) { die('bad $name' } }

We could change that to:

type_check('type', $arg) or die('bad $arg');
...
sub type_check { ($type, $value, $name) = @_; return $type->check($value) }

which is arguably more sensible (although that's of dubious value since it's generated code), and I don't think it's any less efficient. That makes it pretty easy to generate something like:

if (not type_check('type', $arg)) { $arg = type_check('from', $arg) ? $via_code : die('bad $name') }

Or, if it's not desireable to move the die (more precisely, the call to signature_error) into the generated code, we could always just add a parameter to type_check which tells it whether to return false or throw.

Something along those lines.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants