What if macro methods

what if like

as a simple example

struct Foo {
  x: i32,
  y: i32
}

impl Foo {
  fn new(x: i32, y: i32) -> Self {
    Self { x, y }
  }
  macro_rules! scale {
    ($self:expr, $z:expr) => {
      Foo::new($self.x*$z, $self.y*$z)
    };
  }
}

let f = Foo::new(2, 6);
let g = f.scale!(5);
/* let g = Foo::scale!(f, 5); */
/* let g = Foo::new(f.x*5, f.y*5); */

this would be cool and epic and sick and wouldn't interfere with existing features as far as i can tell.

I've worked on postfix macros at https://github.com/rust-lang/rfcs/pull/2442

The idea of making them part of method dispatch, such that they depend on the type of the LHS, would be a much more complicated proposition, because we generally need to fully resolve macros before we have enough information for type dispatch. It's possible to solve that, and there are research languages that solve that, but it'd be a much harder problem.

The approach I used in RFC 2442 was instead to not care about types, just as existing macros don't care about types. (That still leaves room in the future for macros that care about types, whether postfix macros or regular macros.)

6 Likes

Can you say which properties of macros you're interested in here? Do you really want to do macro-like things in them (which depend on the passed-in tokens, or do caller-control-flow-affecting things), or are you just trying to value-like things without needing to write types?

It doesn't help that you appear to never use $z so I don't know what you mean by it.

If you mean the latter, see also the conversations about a hypothetical "macro fn", like Make macro syntax similar to function - #4 by scottmcm

4 Likes

i made two back-to-back typos whoops

    ($self:expr, $z:expr) => {
-     Foo::new($self.x*$self.z, $self.y*$self.z)
+     Foo::new($self.x*$z, $self.y*$z)
    };

the aspect that appeals to me the most is being able to define a DSL-y custom syntax. i'm using macro_rules! for this example but ideally a proc macro would also be possible... somehow. i don't think the whole "proc macros need to be in a seperate crate" works well with being in the middle of an impl block, but i don't have any good ideas atm

consequentially this is interesting in its own right but not precisely the same as what i'm suggesting

I don't think that's a very good motivating example. I'd help if you were able to provide an example where you really want dsl syntax in a method call.

I'm gonna go ahead and say that the standard matches! macro would be really neat as a postfix macro: a.matches!(Some(Ok(1))). Adding method scoping to a specific type seems like it'd require a pretty dramatic amount of changes to the compiler, for seemingly very little benefit.

2 Likes

We can have a separate macro role, called like expression macro that consumes the whole expression token chain prior to its invocation and the arguments to produce token chain that matches expr. -> so that a.method().call!(argument).other_method() is equvivalent to call!(a.method(),argument).other_method()

Was other_method! a typo or should this be:

other_method!(call!(a.method(),argument))

And would this have a trailing comma for the "no arguments" case?

1 Like

It was a typo. The no arguments case i think should have comma in the desugaring - simplifies the matching code?

I agree that the comma should be unconditional. Thanks for the clarification.