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

Implement infix:<//> #151

Closed
masak opened this issue Jun 17, 2016 · 12 comments
Closed

Implement infix:<//> #151

masak opened this issue Jun 17, 2016 · 12 comments

Comments

@masak
Copy link
Owner

masak commented Jun 17, 2016

Whose implementation would simply be

sub infix:<//>(lhs, rhs) {
    if lhs == None {
        return rhs;
    }
    return lhs;
}
@vendethiel
Copy link
Collaborator

no short-circuit?

@masak
Copy link
Owner Author

masak commented Jun 17, 2016

Oh, with short-circuit semantics, of course :-)

@vendethiel
Copy link
Collaborator

ah, so something that'd look like a macro..:)

@masak
Copy link
Owner Author

masak commented Jun 17, 2016

Yeah, it'd have the same mechanism as infix:<&&> and infix:<||> already
employ.

Hm, I wonder how feasible it'd be for an is shortcircuit trait on my
original sub to DTRT...

@vendethiel
Copy link
Collaborator

How would such a trait even look like? Treat its argument as thunks?

@masak
Copy link
Owner Author

masak commented Jun 18, 2016

Well, when the trait is done with it, it certainly can't be a "normal" sub anymore. Here's the original code again for comparison, with the trait added:

sub infix:<//>(lhs, rhs) is shortcircuit {
    if lhs == None {
        return rhs;
    }
    return lhs;
}

And here's the macro I'd expect would be code-gen'd in its place because of the trait:

macro infix:<//>(lhs_ast, rhs_ast) {
    return quasi {
        (sub () {
            my lhs = unquote(lhs_ast);
            if lhs == None {
                return unquote(rhs_ast);
            }
            return lhs;
        })();
    };
}

A couple of stray comments on the above:

  • The main thrust of the code-generation from sub to macro is that the parameters get lazily evaluated. (So maybe the trait should actually be called is lazy?) Case in point, we're only touching rhs_ast if we go into the block in the if. Otherwise it never gets evaluated. That's how we get the short-circuiting.
  • At the same time, we make sure to never evaluate something twice. This is important in the face of side effects in the thunks: we want i++ // 0 to evaluate i++ exactly once. That's why we save the result of unquote(lhs_ast) into a variable lhs. We choose to name-mangle the parameters (which are now ASTs) so that the actual values can get the original names.
  • The anon sub is needed because return has the semantics of leaving a routine early, which we cannot get unless we create a synthetic sub for it.
  • The final () in the IIFE are outside the parenthetical (sub () { ... }) expression to annoy Douglas Crockford.

(By the way, here's my previous attempt at solving this one, which I now feel went too far in some ways and not far enough in others.)

@masak
Copy link
Owner Author

masak commented Jun 18, 2016

(By the way, to any LHF-eager bystanders: this task is still really simple — basically just copy-paste and modify the code for || to make it work like //. We've just hijacked the issue for interesting tangential discussion. We're not sorry.)

@vendethiel
Copy link
Collaborator

I don't understand. By definition, the parameters to the macro are lazily evaluated, since they're just AST slices. That's why a DEBUG macro that only prints its argument if a flag is set can workd

@masak
Copy link
Owner Author

masak commented Jun 18, 2016

All true. Macro params (or rather, the expressions they AST for) are lazily
evaluated. Sub params are not.

The trait is meant to give the author the lazy macro-param semantics, but
without all the other baggage of macros (qtrees, quasis, unquotes, weird
scoping). All one does is write a normal sub, and the macro code is gen'd
behind the scenes.

infix:<//> felt like a good showcase, especially as it was an honest
mistake on my part to leave out the short-circuiting part, and putting it
in would essentially require a re-implementation as a macro... which feels
somewhat disproportionate.

@vendethiel
Copy link
Collaborator

Ah, I'm sorry, I hadn't realized the trait was to be on a sub, not on a macro.
What would the trait give over "thunkish parameters" : http://strangelyconsistent.org/blog/macros-thunkish-parameters ?

@masak
Copy link
Owner Author

masak commented Jun 22, 2016

What would the trait give over "thunkish parameters" http://strangelyconsistent.org/blog/macros-thunkish-parameters ?

That blog post aims to eliminate the macro keyword altogether in favor of marking up parameters as being thunkish. That is silly, since macros do much more than just thunk parameters. Notably, a sub with thunkish parameters would have only the equivalent of the quasi area, not the convenient macro-time area above it. Hence, macros are more powerful/flexible.

More damningly though, the thunkish proposal requires the routine author to think about which parameters are thunks. This is hard, and unnecessary. The proposal in this issue is simply to let that information emerge out of the normal control flow in the sub. Is the rhs parameter a thunk? Yes, because it's only conditionally evaluated inside the block of the if statement. Done; no need to mark things up as thunks.

I felt there was a good idea lurking behind the bad ideas in the "thunkish" post. I think this trait might be it. (Or at least a decent step in the right direction.)

@masak
Copy link
Owner Author

masak commented Jan 29, 2017

Two comments, coming back to this issue far later:

  • I don't know why I insisted on the declaration being sub instead of macro. Maybe to make the thing more harmless than it actually is. As I read this issue now, I don't know if it's morally proper to have a trait on a sub actually throw the sub away and replace it with a macro. Instead, I would be fine, I think, with it being a macro all along. The trait will still have to rip out the whole macro body and put in the code-generated version. (Which I'm fine with.)

  • I just tried to make the generated infix:<//> example above work in the REPL. (Transcript below.) I don't know exactly why it doesn't work, but I suspect it might be related to Make Q::Expr::StatementListAdapter have a value #206. The error messages puzzled me, though.

Transcript:

> macro infix:<??>(lhs_ast, rhs_ast) { return quasi { (sub() { my lhs = {{{lhs_ast}}}; if lhs == None { return {{{rhs_ast}}}; }; return lhs })() }; }; say(1 ?? 2)
Variable 'lhs' is not declared
  in method find-pad at /home/masak/ours/007/lib/_007/Runtime.pm (_007::Runtime) line 83
  in method put-var at /home/masak/ours/007/lib/_007/Runtime.pm (_007::Runtime) line 104
  in method put-value at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 123
  in method run at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 521
  in method run at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 709
  in method call at /home/masak/ours/007/lib/_007/Runtime.pm (_007::Runtime) line 165
  in method eval at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 394
  in method eval at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 393
  in method run at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 539
  in method run at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 709
  in method run at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 585
  in method run at /home/masak/ours/007/lib/_007/Runtime.pm (_007::Runtime) line 26
  in block <unit> at -e line 7

> macro moo(lhs_ast) { return quasi { my lhs = {{{lhs_ast}}}; lhs } }; say(moo(42))
No such method 'eval' for invocant of type 'Q::Block'
  in method eval at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 393
  in method run at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 539
  in method run at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 709
  in method run at /home/masak/ours/007/lib/_007/Q.pm (_007::Q) line 585
  in method run at /home/masak/ours/007/lib/_007/Runtime.pm (_007::Runtime) line 26
  in block <unit> at -e line 7

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

No branches or pull requests

2 participants