Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using percent sign as part of the name for a prefix operator

I thought that %of would be more readable and concise than percent-of for a function name. Here is working code using the longer name.

#!/bin/env perl6

# Quick stats from gene_exp.diff file

sub percent-of
{
    return sprintf('%.1f', (100 * $^a/ $^b).round(0.1));
}

my $total = first-word-from("wc -l       gene_exp.diff ") -1;  # subtract one for the header
my $ok    = first-word-from "grep -c OK  gene_exp.diff";
my $yes   = first-word-from "grep -c yes gene_exp.diff";

put '| total |    OK | OK % |   yes | yes % | yes / OK |';
put "| $total | $ok | { percent-of $ok, $total } | $yes | { percent-of $yes,$total } | { percent-of $yes, $ok } |";



sub first-word-from ( $command )
{
    return ( qqx{ $command } ).words[0];
}

Since I'm putting the subroutine name before its arguments, I would think that this would be a prefix operator. So here is what I thought would work to make the shorter name work (i.e. using sub prefix:<%of> for declaring the function):

#!/bin/env perl6

# Quick stats from gene_exp.diff file

sub prefix:<%of>
{
    return sprintf('%.1f', (100 * $^a/ $^b).round(0.1));
}

my $total = first-word-from("wc -l       gene_exp.diff ") -1;  # subtract one for the header
my $ok    = first-word-from "grep -c OK  gene_exp.diff";
my $yes   = first-word-from "grep -c yes gene_exp.diff";

put '| total |    OK | OK % |   yes | yes % | yes / OK |';
put "| $total | $ok | { %of($ok, $total) } | $yes | { %of($yes,$total) } | { %of($yes,$ok) } |";

sub first-word-from ( $command )
{
    return ( qqx{ $command } ).words[0];
}

But I get the following error:

| total |    OK | OK % |   yes | yes % | yes / OK |
Too few positionals passed; expected 2 arguments but got 1
  in sub prefix:<%of> at /home/bottomsc/bin/gene_exp_stats line 6
  in block <unit> at /home/bottomsc/bin/gene_exp_stats line 15

I'm sure something like what I'm attempting is possible. I've seen much crazier functions than this, like the infix I don't care operator ¯\(°_o)/¯. What am I doing wrong? I get the exact same error when trying calling %of with and without parentheses, so that isn't the issue.

As I was typing up this question, I just realized that I should try following the example just cited and doing this as an infix operator and it worked. However, I'm still curious as to why my prefix operator code didn't work. It's probably something very basic that I'm overlooking.


UPDATE:

Here is the working code when done as an infix operator. But I'm still curious about what I was doing wrong with the prefix version:

#!/bin/env perl6

# Quick stats from gene_exp.diff file

sub infix:<%of>
{
    return sprintf('%.1f', (100 * $^a/ $^b).round(0.1));
}

my $total = first-word-from("wc -l       gene_exp.diff ") -1;  # subtract one for the header
my $ok    = first-word-from "grep -c OK  gene_exp.diff";
my $yes   = first-word-from "grep -c yes gene_exp.diff";

put '| total |    OK | OK % |   yes | yes % | yes / OK |';
put "| $total | $ok | { $ok %of $total } | $yes | { $yes %of $total } | { $yes %of $ok } |";

sub first-word-from ( $command )
{
    return ( qqx{ $command } ).words[0];
}
like image 809
Christopher Bottoms Avatar asked Sep 12 '16 13:09

Christopher Bottoms


2 Answers

I thought that %of would be more readable and concise than percent-of for a function name.

Strongly disagree given this looks exactly like a hash sigil. If I was working with you I would go nuts if I ever came across this. What's wrong with percent-of as an infix? 5 percent-of 100 returning 0.05 or something?

I read %of as hash called of. Never in a million years when reading Perl will I think % as a newly defined operator meaning 'percent' prefixed like that. So it's not readable given the language. It being concise isn't really a great justification either, it has much greater cognitive load so is again less readable. I'm not entirely sure what the balance to that criticism is? Other than it's frightening and cool you can do it at all...

like image 138
Matt Oates Avatar answered Oct 14 '22 05:10

Matt Oates


Calling a prefix op with two arguments

An opening parenthesis is only interpreted as a function call when it follows a function name, not when it follows a prefix operator name.

So in your second code snippet, this expression does not call the %of operator with two arguments:

%of($ok, $total)

Instead, it creates a List value with two elements, and then applies the %of operator to that single List value - hence the error message "expected 2 arguments but got 1".

In order to pass multiple arguments to a prefix operator, you have to invoke its fully qualified name:

prefix:<%of>($ok, $total);

Of course this negates your goal of "readable and concise".

Using a single argument

If you really want to use a prefix operator for this, you can make it accept a single List parameter, and unpack it into two values right in the signature, by using a [ ] subsignature:

sub prefix:<%of> ([$a, $b]) {
    sprintf '%.1f', (100 * $a / $b).round(0.1);
}

say %of(42, 50);  # 84.0

Advice

Note that declaring an operator that looks like a hash variable will likely confuse the next person who has to maintain your code (maybe even yourself, when you come back to it in a few weeks).

The built-in prefix operators like ++ or ~ have restricted themselves to one or two non-letter symbols for good reason. Perl 6 doesn't place the same restriction on user-defined operators, but remember that with great power comes great responsibility... :)

like image 25
smls Avatar answered Oct 14 '22 06:10

smls