Skip to content

Instantly share code, notes, and snippets.

@masak
Created October 20, 2013 20:31
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save masak/7074924 to your computer and use it in GitHub Desktop.
Save masak/7074924 to your computer and use it in GitHub Desktop.
Use cases for macros

So far only one use case, because (a) I haven't had time to write about the other ones, and (b) this was the one that came to mind quite strongly today.

The each junction

There has been talk about this junction now and then; both on the #perl6 channel and in S09. Its semantics is pretty clear: put a list in a position where a scalar is expected, causing the statement to be executed for each of the list's elements.

%hash{each <foo bar baz>} = True;       # assign each hash key

promote(each @employees);               # call with each element as argument

@results.push(&filter(each @inputs));   # filter and push each input

each feels like a good use case for a macro to me.

The interesting thing about each from the viewpoint of a macro is that each itself would mainly be saying "OK, turn the current statement into an 'each-y' statement". The main action would then have to happen after the entire statement was processed: wrapping the whole statement in a for loop with a gensym'd loop variable, and replacing the each with that loop variable.

The only question is how to inject that macro at the statement level. That we don't have a feature for, as far as I know. But I could see it being useful for all kinds of DSL-y things. I picture that feature as being akin to AOP's cutpoints, or Git's hooks: being able to inject logic into predefined points of the existing bits of compiler architecture.

Oh, and while we're talking about each. I wonder what the semantics should be when there are several each expressions in a statement. I can see several answers to that:

  1. It's disallowed. Two eaches is complicated enough! Doesn't feel very Perl-y to put up such a limit, though.
  2. Nested loops, in strict parse order, innermost loop first. Parse order is the way variable declaration/use works — you have to declare a variable on first use. So say each(1, 2) + each(10, 20) would output 11 12 21 22. The fact that I have to think hard here whether I like "innermost loop first" or "outermost loop first" doesn't speak well for the complexity of this alternative.
  3. Nested loops, in evaluation order, innermost loops first. Would output the same for say each(1, 2) + each(10, 20) as the previous option, but would differ in say each(1, 2) + $_ given each (10, 20) because the given expression is evaluated before the say statement. Makes a certain amount of sense. The fact that I can't really decide between alternatives 2 and 3 also doesn't speak well for the complexity of these alternatives.
  4. Run in parallel, just like infix:<Z>. So say each(1, 2) + each(10, 20) would output 11 22.

Anyway. Did not paste this to discuss semantics — those bits came unbidden to me. But I do think each fits nicely into what we should expect from Perl 6 macros if we want them to be useful and stand apart from subroutines.

@raiph
Copy link

raiph commented Oct 21, 2013

So, helper functions for macros like each that:

-- Register some code to be run after the enclosing ast node (of type such-and-such) has been make'd.

-- Insert new ast before or after that existing ast node. (To be used by the code registered per 1).

?

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