Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Pondering parameterized operators

1 view
Skip to first unread message

Austin Hastings

unread,
Sep 26, 2003, 4:34:55 PM9/26/03
to perl6-l...@perl.org
So I sit here and think for a minute about how nice it will be in P6 to
be able to define

operator infix:eqi($str1, $str2) {...}

for doing

if ($1 eqi "last")

and I think about the whole 'C' string library. Which dredges up my old
questions about parameterized operators:

How can I conveniently pass an extra parameter to a historically binary
operator?

At one point, the colon was going to do that, but Larry took it back:

if ("Dough" eqn:4 "Douglas") # true
{ ...

The options left seem to be:

package String;
sub strncmp($a, $b, $n) {...}
package main;

sub infix:immed &String::strncmp.assuming($n = 4);
if ("Dough" immed "Douglas") # ?

or:

class InfixBinary is Code {...}
# How do I create a "type"? -- I want to define a return-type-spec
# that just guarantees context behavior.

sub eqn($max) returns InfixBinary {
return &String::strncmp.assuming($n=$max);
}

if ("Dough" eqn(4) "Douglas") ...

Frankly, I like the second one.

=Austin

Dan Sugalski

unread,
Sep 26, 2003, 4:36:42 PM9/26/03
to Austin Hastings, perl6-l...@perl.org
On Fri, 26 Sep 2003, Austin Hastings wrote:

> How can I conveniently pass an extra parameter to a historically binary
> operator?

If it's one of the 'base' binary operators (addition, subtraction, and
whatnot) you don't.

Dan

Luke Palmer

unread,
Sep 26, 2003, 6:16:29 PM9/26/03
to Austin Hastings, perl6-l...@perl.org
Austin Hastings writes:
> How can I conveniently pass an extra parameter to a historically binary
> operator?

I see a few possibilities. The first, call it like a function:

if infix:eqn("Dough", "Douglas", n => 4) {...}

Or, you could use the adverbial modifier C<where> (well, not officially
yet, but I hope so):

if "Dough" eqn "Douglas" where n=>4 {...}

But that has some problems, like putting the operator parameters really
far away from the operator, and forcing parentheses on a function call
on the right (lest the C<where> be associated with the call).

More below.

> At one point, the colon was going to do that, but Larry took it back:
>
> if ("Dough" eqn:4 "Douglas") # true
> { ...
>
> The options left seem to be:
>
> package String;
> sub strncmp($a, $b, $n) {...}
> package main;
>
> sub infix:immed &String::strncmp.assuming($n = 4);

You probably mean:

our &infix:immed ::= &String::strncmp.assuming($n => 4);

> if ("Dough" immed "Douglas") # ?
>
> or:
>
> class InfixBinary is Code {...}
> # How do I create a "type"? -- I want to define a return-type-spec
> # that just guarantees context behavior.
>
> sub eqn($max) returns InfixBinary {
> return &String::strncmp.assuming($n=$max);
> }
>
> if ("Dough" eqn(4) "Douglas") ...
>
> Frankly, I like the second one.

Yeah, I don't think it's a bad idea. I think returning an infix
operator is not the way to go about it. Perhaps optional (and named)
parameters on an infix operator could be taken from a parameter list:

sub infix:eqn ($a, $b, ?$n) {
substr($a, 0, $n) eq substr($b, 0, $n)
}

if "Dough" eqn(4) "Douglas" {...}

That's not terribly good at making different things look different.
It's pretty common to see parenthesized expressions on either side of an
operator.

Hmm, since we're requiring no whitespace between a variable and it's
subscript, this should be possible:

if "Dough" [eqn 4] "Douglas" {...}

If we're going that far...

sub foo($a, $b) {
print "$($a)y$b";
}
"Dough" [foo] "Douglas"; # DoughyDouglas

Not sure I like that too well.

Luke

Dave Whipp

unread,
Sep 26, 2003, 9:07:46 PM9/26/03
to perl6-l...@perl.org
"Austin Hastings" <austin_...@yahoo.com> wrote

> if ("Dough" eqn(4) "Douglas") ...

I wonder if the . operator is available here:

if "Dough" eq.n(4) "Douglas" { ... }

that makes it intuitive how to define new equality "methods". One thing of
concern is that we'd need whitespace rules to disambiguate things. I sense
some wonderful opportunities for obfuscation.


Dave.


Eirik Berg Hanssen

unread,
Sep 27, 2003, 12:35:22 PM9/27/03
to perl6-l...@perl.org
Luke Palmer <fibo...@babylonia.flatirons.org> writes:

> Hmm, since we're requiring no whitespace between a variable and it's
> subscript, this should be possible:
>
> if "Dough" [eqn 4] "Douglas" {...}

Lisp! :-)

Well, almost. Now this would be lisp-y:

if $test [$moon.is_waxing ? &infix:< : &infix:>=] $target {...}


Let us see ... somewhat speculative and probably short-of-the-mark
generalization coming up:


macro infix:[ ($lhs, $op, $rhs)
is parsed(/(<Perl6.expr>) \] (<Perl6.expr>)/) {
return {
$op($lhs, $rhs)
};
}

(Precedence? Err ... the left hand side has already been parsed,
so infix:[ must be of fixed precedence to the left hand side, right?
Damn, I thought I had it ...)

Then vector operators, like >>+<<, are "really" just simple
[vectorize &infix:+] and similar -- except properly optimized and
presumably with proper precedence. And, you can write:


if "Dough" [&String::strncmp.assuming(n => 4)] "Douglas" {...}


Still long. Oh well. infix:[eqn, perhaps? At least that one will
be of fixed precedence. Right?


macro infix:[eqn ($lhs, $n, $rhs)
is equiv(&infix:eq)
is parsed(/(<Perl6.expr>)
\]
(<Perl6.expr but speculatively_equiv(&infix:eq)>)/) {
return {
String::strncmp($lhs, $rhs, n => $n)
};
}

Eirik
--
"So this is the Sword of Immortality? Huh?
What's it doin' in a CRYPT?!"
--- John S. Novak, III, quoting an unnamed player

Austin Hastings

unread,
Sep 27, 2003, 6:16:38 PM9/27/03
to perl6-l...@perl.org

> -----Original Message-----
> From: Eirik Berg Hanssen [mailto:Eirik-Ber...@allverden.no]
> Sent: Saturday, September 27, 2003 11:35 AM
> To: perl6-l...@perl.org
> Subject: Re: Pondering parameterized operators
>
>
> Luke Palmer <fibo...@babylonia.flatirons.org> writes:
>
> > Hmm, since we're requiring no whitespace between a variable and it's
> > subscript, this should be possible:
> >
> > if "Dough" [eqn 4] "Douglas" {...}
>
> Lisp! :-)

No, just horribly early evaluation.

That'll do, Luke. That'll do.

> if $test [$moon.is_waxing ? &infix:< : &infix:>=] $target {...}

This is right, presuming we have a way to give the parser the right data
(function properties).

> Let us see ... somewhat speculative and probably short-of-the-mark
> generalization coming up:
>
>
> macro infix:[ ($lhs, $op, $rhs)
> is parsed(/(<Perl6.expr>) \] (<Perl6.expr>)/) {
> return {
> $op($lhs, $rhs)
> };
> }
>
> (Precedence? Err ... the left hand side has already been parsed,
> so infix:[ must be of fixed precedence to the left hand side, right?
> Damn, I thought I had it ...)

This is text replacement, not expression evaluation. You do have it from
where I sit.

# Note: Need a way to parse nested []'s
macro [ ($whosit) is parsed(/(<?:\s[) (<expr>) \]/) {
eval $whosit;
}

The macro immediately evaluates the expression, so it has to be a deferrable
reference.
Then:

macro infix:eqn($n) is equiv (&infix:eq) {
"[&String::strncmp.assuming(n => 4)]"
}

so


if "Dough" eqn(4) "Douglas" {...}

becomes
if "Dough" [sub &String::strncmp.assuming(n => 4)] "Douglas" {...}
becomes
if "Dough" <temporary infix-binary inserted by macro> "Douglas" {...}

and it just works. Woo-hoo!

> Then vector operators, like >>+<<, are "really" just simple
> [vectorize &infix:+] and similar -- except properly optimized and
> presumably with proper precedence. And, you can write:

> if "Dough" [&String::strncmp.assuming(n => 4)] "Douglas" {...}

> Still long. Oh well. infix:[eqn, perhaps? At least that one will
> be of fixed precedence. Right?

See above. No precedence worries with an "unattached" macro -- do a text
replacement
or give the parser a correctly attributed tree, and let it worry about it.

=Austin

Luke Palmer

unread,
Sep 27, 2003, 10:02:06 PM9/27/03
to Eirik Berg Hanssen, perl6-l...@perl.org
Cool stuff. In my usual pedantic sort of way, I'll go through the
message and fix everything you said. For educational purposes only, of
course :-)

Eirik Berg Hanssen writes:
> Luke Palmer <fibo...@babylonia.flatirons.org> writes:
>
> > Hmm, since we're requiring no whitespace between a variable and it's
> > subscript, this should be possible:
> >
> > if "Dough" [eqn 4] "Douglas" {...}
>
> Lisp! :-)
>
> Well, almost. Now this would be lisp-y:
>
> if $test [$moon.is_waxing ? &infix:< : &infix:>=] $target {...}

if $test [$moon.is_waxing ?? &infix:< :: &infix:>=] $target {...}

> Let us see ... somewhat speculative and probably short-of-the-mark
> generalization coming up:
>
>
> macro infix:[ ($lhs, $op, $rhs)
> is parsed(/(<Perl6.expr>) \] (<Perl6.expr>)/) {
> return {
> $op($lhs, $rhs)
> };
> }

Pretty close.

# Let's say C<is parsed> args come before the default infix args
macro infix:[ ($op, $lhs, $rhs)
is parsed( /:w (<Perl6.expr>) \]/ )
{
my ($clhs, $crhs) = [$lhs, $rhs]損.束compile; #only compile once
return {
$op($clhs.run, $crhs.run);
}
}

This would have the precedence of &infix:+ .

> (Precedence? Err ... the left hand side has already been parsed,
> so infix:[ must be of fixed precedence to the left hand side, right?
> Damn, I thought I had it ...)

You shouldn't parse the right hand side yourself. That fixes the issue.
Note that this is just how ??:: is going to do things:

macro infix:?? ($iftrue, $cond, $iffalse)
is parsed( /:w (<Perl6.expr>) <'::'>/ )
is tighter(&infix:=)
is assoc('right')
{
my ($ccond, $ciftrue, $iffalse) = [$iftrue, $cond, $iffalse]損.束compile;
return {
if $ccond.run {
$ciftrue.run;
}
else {
$ciffalse.run;
}
}
}

And there might just be a C<is compiled> trait to automatically do that
first line. If not, it will surely be defined in Macro::Common. :-)

> Then vector operators, like >>+<<, are "really" just simple
> [vectorize &infix:+] and similar -- except properly optimized and
> presumably with proper precedence.

Yeah. Very cool.

> And, you can write:
>
>
> if "Dough" [&String::strncmp.assuming(n => 4)] "Douglas" {...}
>
>
> Still long. Oh well. infix:[eqn, perhaps?

Do you I<want> to give me a heart attack? :-/

This would be fine, I think:

sub eqn ($n) { &String::strncmp.assuming(n => $n) }

> At least that one will be of fixed precedence. Right?
>
>
> macro infix:[eqn ($lhs, $n, $rhs)
> is equiv(&infix:eq)
> is parsed(/(<Perl6.expr>)
> \]
> (<Perl6.expr but speculatively_equiv(&infix:eq)>)/) {
> return {
> String::strncmp($lhs, $rhs, n => $n)
> };
> }

I can't feel my left arm...

Again, you don't need to parse the right side of the operator yourself,
otherwise it be a postfix: macro.

Luke

Eirik Berg Hanssen

unread,
Sep 27, 2003, 8:38:13 PM9/27/03
to perl6-l...@perl.org
"Austin Hastings" <Austin_...@Yahoo.com> writes:

> [Eirik wrote:]


> > Let us see ... somewhat speculative and probably short-of-the-mark
> > generalization coming up:
> >
> >
> > macro infix:[ ($lhs, $op, $rhs)
> > is parsed(/(<Perl6.expr>) \] (<Perl6.expr>)/) {
> > return {
> > $op($lhs, $rhs)
> > };
> > }
> >
> > (Precedence? Err ... the left hand side has already been parsed,
> > so infix:[ must be of fixed precedence to the left hand side, right?
> > Damn, I thought I had it ...)
>
> This is text replacement, not expression evaluation. You do have it from
> where I sit.

Text replacement? This is returning a closure, not text. But even
if this is a case of text replacement, I get the first parameter,
$lhs, from text that has already been parsed.


> # Note: Need a way to parse nested []'s
> macro [ ($whosit) is parsed(/(<?:\s[) (<expr>) \]/) {
> eval $whosit;
> }

I may not be up-to-date on macros and is parsed, but ITYM:

macro prefix:[ ($whosit) is parsed(/(<Perl6.expr>) \]/) {
return eval $whosit;
}

This should return text, I believe. But the parser will not be
expecting a term (or prefix:-operator) here ... it is looking for
an infix:-operator or a postfix:-operator, right?

(And it will eval() at compile time, so my phase-of-the-moon example
will not be evaluated at run time, even if this works.)


> The macro immediately evaluates the expression, so it has to be a deferrable
> reference.
> Then:
>
> macro infix:eqn($n) is equiv (&infix:eq) {
> "[&String::strncmp.assuming(n => 4)]"
> }

(s/4/\$n/, I believe.)

But the infix:-operators by default get their LHS and RHS parse
trees as parameters, last I heard, so your parameter list is too
short. Still, you may be on to something ... what about this?

macro infix:eqn ($lhs, $n, $rsh) is equiv(&infix:eq)
is parsed(/ \( (<Perl6.expr>) \)


(<Perl6.expr but speculatively_equiv(&infix:eq)>)/) {

return "String::strncmp($lhs, ($rhs), ($n))";
}

if "Dough" eqn(4) "Douglas" {...}
becomes

if String::strncmp("Dough", ("Douglas"), (4)) {...}

Okay, that may be evil. But it DWYW, right?


No such luck with my phase-of-the-moon example, though. And come to
think of it, finding the right precedence might be tricky, even if
this (or more complicated re-parsing tricks) should happen to work:

macro infix:[ ($lhs, Sub $op, $rhs)
is equiv($op)
is parsed(/(<Perl6.expr>)
\]
(<Perl6.expr but speculatively_equiv($op)>)/) {
return "($op).(($lhs), ($rhs))";
}

# This might be determinable at compile time:


if $test [$moon.is_waxing ? &infix:< : &infix:>=] $target {...}

# This would need explicit "but equiv" or something like that:
if $test [($lookup{$key} || &infix:eq) but equiv(&infix:eq)] $target {...}


So, probably just a bad idea.


Eirik
--
All bridge hands are equally likely, but some are more equally likely
than others.
-- Alan Truscott

Simon Cozens

unread,
Sep 28, 2003, 5:01:39 AM9/28/03
to perl6-l...@perl.org
fibo...@babylonia.flatirons.org (Luke Palmer) writes:
> [$lhs, $rhs]æ\220\215.æ\235\237compile;

What's that in old money?

--
As the saying goes, if you give a man a fish, he eats for a day. If you
teach him to grep for fish, he'll leave you alone all weekend. If you
encourage him to beg for fish, pretty soon c.l.p.misc will smell like a
three-week-dead trout. -- Tom Phoenix, c.l.p.misc.

Luke Palmer

unread,
Sep 28, 2003, 10:47:47 AM9/28/03
to Simon Cozens, perl6-l...@perl.org
Simon Cozens writes:
> fibo...@babylonia.flatirons.org (Luke Palmer) writes:
> > [$lhs, $rhs]脱\220\215.脱\235\237compile;

>
> What's that in old money?

Eep. I thought I had mutt's utf-8 working, but apparently not.

Does anyone who knows about these things want to mail me instructions,
or something? That'd be nice for most everyone who reads my posts :-)

Luke

[$lhs, $rhs]>>.<<compile;

Eirik Berg Hanssen

unread,
Sep 28, 2003, 11:52:13 AM9/28/03
to perl6-l...@perl.org
Luke Palmer <fibo...@babylonia.flatirons.org> writes:

> Cool stuff. In my usual pedantic sort of way, I'll go through the
> message and fix everything you said. For educational purposes only, of
> course :-)

Thanks :-)

For further education, some more questions/comments, if I may:


> # Let's say C<is parsed> args come before the default infix args
> macro infix:[ ($op, $lhs, $rhs)
> is parsed( /:w (<Perl6.expr>) \]/ )

[snip]

> You shouldn't parse the right hand side yourself. That fixes the issue.

I was not aware of this option. A6/E6 gave me the impression that
C<is parsed> would override the default parsing of the RHS, not merely
augment it. Does the parsed-trait somehow interact with the signature
of the macro, to find out whether to extend or replace?

That is, can I write prefix:-macros that do not take the default RHS
parameters, but only the ones generated by C<is parsed>? Quotelike
operators come to mind:

macro prefix:qtex ($raw)
is parsed( /(<Perl6.delimited_balanced .*?>)/ ) { ... }

Or would that be accomplished by another keyword, indicating what,
in the absence of C<is parsed>, would be 0-ary? term:qtex, perhaps?
I guess that depends upon what kind of consistency you are aiming for.


> Do you I<want> to give me a heart attack? :-/

Nope. I will try to restrain myself.

> This would be fine, I think:
>
> sub eqn ($n) { &String::strncmp.assuming(n => $n) }

You mean it would be fine for using with the infix:[ macro, right?
But then you could not get the precedence you want. You would be
stuck with whatever precedence infix:[ was given.

Luke Palmer

unread,
Sep 28, 2003, 9:42:09 PM9/28/03
to Eirik Berg Hanssen, perl6-l...@perl.org
Eirik Berg Hanssen writes:
> Luke Palmer <fibo...@babylonia.flatirons.org> writes:
> > You shouldn't parse the right hand side yourself. That fixes the
> > issue.
>
> I was not aware of this option. A6/E6 gave me the impression that
> C<is parsed> would override the default parsing of the RHS, not
> merely augment it. Does the parsed-trait somehow interact with the
> signature of the macro, to find out whether to extend or replace?

Hmm.

For an ordinary list operator, we have a rule something like this:

rule listop() {:w
<listop_id>
[ <parglist> | <arglist> ]
}

I can imagine that, in the presence of C<is parsed>, it look more like:

rule listop() {:w
<listop_id>
<{ $listop_id.parsed // /<parglist> | <arglist>/ }>
}

This isn't a problem, as it's a rule tail-call.

Now let's look at how operators might be done. It's possible to store
precedence in a linked list of collections of operators, so:

sub infix:+ () {...} # Default
sub infix:* () is tighter(&infix:+) {...}
sub infix:/ () is same(&infix:*) {...}
sub infix:** () is tighter(&infix:*) {...}
sub infix:== () is looser(&infix:+) {...}

Makes the list:

/==/ -> /\+/ -> /* | \// -> /\*\*/

Associativity would be stored in the list frames, too.

If you're doing bottom up parsing, this is a terrible way to do things
for efficiency reasons. But it's quite nice for top down.

rule infixop($prec) {<{
given $prec.assoc {
when 'left' { # Let's just pretend left recursion is possible
/:w <infixop($prec)> <$prec.ops> <infixop($prec.next)> /
}
when 'right' {
/:w <infixop($prec.next)> <$prec.ops> <infixop($prec)> /
}
when 'non' {
/:w <infixop($prec.next)> <$prec.ops> <infixop($prec.next)> /
}
}
} | <infixop($prec.next)>
}

Among this list also lives the prefixes and postfixes, complicating
these rules somewhat. We'll ignore these for now.

Now you can see that allowing an C<is parsed> rule on operators to grab
the rhs would screw up its precedence, unless the programmer could
explicitly call C<< <infixop($prec.next)> >>. That can be done to some
extent with your speculatively_equiv property, but still one must take
great care to keep from making a mess of the parse tree.

> That is, can I write prefix:-macros that do not take the default RHS
> parameters, but only the ones generated by C<is parsed>? Quotelike
> operators come to mind:
>
> macro prefix:qtex ($raw)
> is parsed( /(<Perl6.delimited_balanced .*?>)/ ) { ... }
>
> Or would that be accomplished by another keyword, indicating what,
> in the absence of C<is parsed>, would be 0-ary? term:qtex, perhaps?

Yeah. Or circumfix:, but that doesn't work for custom quoting
operators. term: would be pretty nice for custom parsing rules,
actually.

> I guess that depends upon what kind of consistency you are aiming
> for.
>
>
> > Do you I<want> to give me a heart attack? :-/
>
> Nope. I will try to restrain myself.
>
> > This would be fine, I think:
> >
> > sub eqn ($n) { &String::strncmp.assuming(n => $n) }
>
> You mean it would be fine for using with the infix:[ macro, right?
> But then you could not get the precedence you want. You would be
> stuck with whatever precedence infix:[ was given.

Yeah, that's true. But note that you can't do that black magic you were
speaking of earlier:

$a [ (cond ?? &infix:+ : &infix:*) but tighter(&infix:*) ] $b

Because we can't choose the precedence of an operator after we parse
it... unless we're doing bottom up, which we're not. I do think that
the infix [] construct would have to have a fixed precedence.

Luke

Austin Hastings

unread,
Sep 29, 2003, 11:46:18 AM9/29/03
to Luke Palmer, perl6-l...@perl.org

--- Luke Palmer <fibo...@babylonia.flatirons.org> wrote:
> Yeah, that's true. But note that you can't do that black magic you
> were
> speaking of earlier:
>
> $a [ (cond ?? &infix:+ : &infix:*) but tighter(&infix:*) ] $b
>
> Because we can't choose the precedence of an operator after we parse
> it... unless we're doing bottom up, which we're not. I do think that
> the infix [] construct would have to have a fixed precedence.
>

Run time or compile time? If the [] is a compile time operation, it
doesn't matter, of course, since everything is done.

If the precedence of [] can vary, or if user-ops can vary their
precedence, the parser will have to have some sort of "late
reevaluation" mechanism.

This fits in with the "recompile if you change the world" thread from
last week, I guess.

It also ties in kind of nicely with the "feel" of Damian's OOPerl book:
making objects out of unlikely things. In this case, we're making
operators out of them.

my &op1;
my &op2;

sub foo {
my $x = $^a &op1 $^b &op2 $^c;
print "Foo: " . $x . "\n";
}

# (Luke: Because these are literal lists, I don't need >>:=<<,
right?)
(&op1, &op2) := (&infix:+, &infix:*);
foo(3,4,5);
# Prints 23

(&op1, &op2) := (&op2, &op1);
foo(3,4,5);
# Prints 17 (27 if the parser doesn't rescan :-)

=Austin

0 new messages