Calling functions with different numbers of arguments in Rust macros - macros

I need a macro that will call functions with different numbers of arguments or a macro that will generate a valid argument list from its (repeating) parameters.
I am fine with explicitly giving the information about the number of arguments to the macro, but I can't figure out how to generate the argument list for the function - I always stumble on the macros returning expressions rather than token tree.
I made the following playground example:
macro_rules! call (
($f: expr, $($params:tt)*) => {
$f(make_params!($($params:tt)*))
};
);
macro_rules! make_params {
() => {};
(I $($params: tt)*) => {
1, make_params!($($params:tt)*)
};
}
fn foo(a: i32, b: i32, c: i32) {
println!("foo: {} {} {}", a, b, c);
}
fn bar(a: i32, b: i32) {
println!("bar: {} {}", a, b);
}
fn main() {
call!(foo, I I I);
call!(bar, I I);
}
The compiler complains with the following:
error: macro expansion ignores token `,` and any following
--> src/main.rs:10:10
|
10 | 1, make_params!($($params:tt)*)
| ^
|
note: caused by the macro expansion here; the usage of `make_params!` is likely invalid in expression context
--> src/main.rs:3:12
|
3 | $f(make_params!($($params:tt)*))
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
...
How can I treat the return of make_params! as a token stream (or such) rather than expression?
My real use case is a bit more involved than this toy example. My functions have multiple parameter types which are constructed in different ways. In my case, just making macros call1, call2!, ... does not seem like a good solution, as I would need the likes of call_IIOOI, call_IIIO, etc.

You need to build the function call progressively as you go and only emit it at once in the end:
macro_rules! call (
($f: expr, $($params:tt)*) => {
make_call!($f, () $($params)*)
};
);
macro_rules! make_call {
($f: expr, ($($args:tt)*)) => { $f($($args)*) };
($f: expr, () I $($params:tt)*) => {
make_call!($f, (1) $($params)*)
};
($f: expr, ($($args:tt)*) I $($params:tt)*) => {
make_call!($f, ($($args)*, 1) $($params)*)
};
}
playground

Related

Understanding what a function is

Background
I tried to answer a question "what is function?" and wonder if I actually know what it is. Please help to understand what a "function" is in Scala. It may sound non-sense debate but please be patient to help.
Questions
1. What is function
A "function" is a "computation/operation" to be "applied" to a single "argument" to generate an "value". If there are multiple argument, then it can be converted into ()()()... which is called currying.
w is a name
b is a binding of a function object to a name
c is computation
a is application
g is argument on which apply an compuation
val w = ( ) => { }
^ ^ ^ ^ ^
| | | | |
(n) (b) (g) (a) (c)
Is it OK to say these?
Also if this is to apply a computation to an argument:
() => { }
Then it actually should be in the opposite direction?
() <= { }
or
{ } => ()
2. Decomposition of definition
Is this correct understanding of what "def f (x:Unit):Unit = {}" is?
//--------------------------------------------------------------------------------
// The function literal says:
// 1. Define a "function" (ignore "method" here).
// 2. Bind the function to a name f.
// 3. It is to be applied to an "argument" of type Unit.
// 4. Bind the argument to a name x.
// 5. E-valuation, or appliation of the function to an argument generates an "value" of type Unit.
// 6. After evaluation, substitute it with the "value".
//--------------------------------------------------------------------------------
def f (x:Unit):Unit = {}
3. Evaluation / Application
Is "evaluation" the same with "application of a function to an argument and yield an value"? When I read lambda calculas, word "application" is used, but I think "evaluation" is also used.
Unit
//--------------------------------------------------------------------------------
// The literal says:
// 1. Apply the function f
// 2. on the "argument" enclosed between '(' and ')', which is Unit.
// 3. and yield Unit as the "value" evaluated.
//--------------------------------------------------------------------------------
def f (x:Unit):Unit = {}
f()
Is it the same with this? If so is "Unit" an object?
f(Unit) // No error in Scala worksheet
What is causing the error "Too many arguments" for Unit as argument below?
// Define a function that applies on an argument x of type Unit to generate Unit
() => {} // res0: () => Unit = <function0>
(() => {}) // res1: () => Unit = <function0>
// Application
(() => {})()
/* Error: Too many arguments */
(() => {})(Unit)
4. Referential transparency
Please advise if this is correct.
Using "def g (x:String): Unit = println(x)" as an example, "referential transparency" means that g(x) can be always substituted with its result and it will not break any.
If
g("foo")
can be always replaced with
Unit
then it is referentially transparent. However, it is not the case here for g. Hence g is not a referentially transparent function, hence it is not "pure" function. Random is also not pure.
{ scala.util.Random.nextInt } // res0: Int = -487407277
In Scala, function can be pure or side-effective. There is no way to tell by just having look at a function. Or is there a way to mark as such, or validate if it is pure or not?
5. Method is not a function
A method cannot be a first class object to be passed around but it can be by converting it to a function.
def g (x:String): Unit = println(x)
g("foo")
val _g = g _
_g("foo")
Why method cannot be a first class object? If a method is an object, what will happen or what will break?
Scala compiler does clever inferences or complehentions, then if it can be converted into an object with _, why Scala does not make it a firt class object?
6. What is { ... }?
Update:
"=> T" is call by name passing expression to be evaluated inside the function, hence has nothing to do with "{...}" specifically. {...} is a block expression. Hence all below is invalid.
It looks "{...}" is the same with "=> T".
def fill[T](n: Int)(elem: => T)
Array.fill[Int](3)({ scala.util.Random.nextInt })
{...} in itself yield an value without taking any argument.
{ scala.util.Random.nextInt } // res0: Int = 951666328
{ 1 } // res1: Int = 1
Does it mean "application" is an independent first class object, or the Scala compiler is clever enough to understand it is an abbreviation of:
() => { scala.util.Random.nextInt }
or
val next = (x:Int) => { scala.util.Random.nextInt(x) }
If so, "=> T" is actually "() => T"?
In Scala function is an implementation of one of traits from Function1 to Function22 depending on input parameters amount. For your particular example w is a shorthand for anonfunW:
val w = () => {}
val anonfunW = new Function1[Unit, Unit] {
def apply(x: Unit): Unit = ()
}

Use output of macro as parameter for another macro

I am trying to implement a generic Point<T> type for small dimensions.
To achieve that, I wrote a macro that takes the name of the new type and the dimension of the point (since, as far as I know, Rust doesn't allow for numerical generics).
macro_rules! define_point {
($type_name: ident, $dimension: expr) => {
pub struct $type_name<T> {
coords: [T; $dimension]
}
}
}
I use it like this:
define_point!(Point2, 2);
define_point!(Point3, 3);
This works fine. I also implement the Index trait on my Point type in this macro to access the coordinates directly.
Now I want some convenience functions for accessing the coordinates of my point as follows: p.x(), p.y() or p.z() depending on the dimension.
To do that, I have another macro:
macro_rules! impl_point_accessors {
($type_name: ident, $coord_name: ident, $coord_index: expr) => {
impl<T> $type_name<T> {
pub fn $coord_name(&self) -> T {
&self[$coord_index]
}
}
};
($type_name: ident, $coord_name: ident, $coord_index: expr, $($extra_coord_name: ident, $extra_coord_index: expr),+) => {
impl_point_accessors!($type_name, $coord_name, $coord_index);
impl_point_accessors!($type_name, $($extra_coord_name, $extra_coord_index), +);
}
}
I use it as follows:
impl_point_accessors!(Point2, x, 0, y, 1);
impl_point_accessors!(Point3, x, 0, y, 1, z, 2);
This seems to work when I look at the result of rustc --pretty=expanded.
Now, as an exercise, I wrote this other macro that would give me the list x, 0, y, 1, ... from the dimension directly:
macro_rules! dimension_to_coord_pairs {
(1) => {
x, 0
};
(2) => {
x, 0, y, 1
};
(3) => {
x, 0, y, 1, z, 2
};
(4) => {
x, 0, y, 1, z, 2, w, 3
};
}
However, when I try to use the output of this new macro like this:
impl_point_accessors!($type_name, dimension_to_coord_pairs!($dimension));
It looks like the dimension_to_coord_pairs macro does not get expanded into the list of arguments that I want.
My question now: Is there any way to tell Rust to expand the macro and use the expanded syntax as my list of arguments in another macro ?
A macro can invoke another macro, but one cannot take the result of another. Each macro invocation must result in legal code, which can include other macro invocations, but the compiler should never have to figure out which macro you intended to be invoked first.
You can work around your problem by reorganising the macros to be completely top-down, something like this:
macro_rules! define_point {
($type_name: ident, $dimension: tt) => {
pub struct $type_name<T> {
coords: [T; $dimension]
}
impl_point_accessors!($type_name, $dimension);
}
}
macro_rules! impl_point_accessors {
($type_name: ident, $dimension: tt) => {
impl<T> $type_name<T> {
write_coord_getters!($dimension);
}
};
}
macro_rules! coord_getter {
($coord_name: ident, $coord_index: expr, $ret: ty) => {
pub fn $coord_name(&self) -> &T {
&self.coords[$coord_index]
}
}
}
macro_rules! write_coord_getters {
(1) => {
coord_getter!(x, 1, T);
};
(2) => {
write_coord_getters!(1);
coord_getter!(y, 2, T);
};
(3) => {
write_coord_getters!(2);
coord_getter!(z, 3, T);
};
(4) => {
write_coord_getters!(3);
coord_getter!(w, 4, T);
};
}
It's not quite as tidy as you were attempting, but it still lets you invoke it the way you wanted:
define_point!(Point3, 3);
Notice that I changed $dimension: expr to $dimension: tt. I'm not 100% sure why this is the case but, inside a macro, a variable of type expr cannot match a literal.
Also, I changed the return type to &T instead of T. You could also fix the same problem by making T: Copy instead.
My question now: Is there any way to tell Rust to expand the macro and use the expanded syntax as my list of arguments in another macro ?
No. Macros are syntactic, not lexical. That is, a macro cannot expand to an arbitrary bundle of tokens. Even if it could, you would need some way to force the compiler to expand the inner macro before the outer one, and you can't do that either.
The closest you can get is to use a "callback" style:
macro_rules! impl_point_accessors {
($type_name: ident, $coord_name: ident, $coord_index: expr) => {
impl<T> $type_name<T> {
pub fn $coord_name(&self) -> T {
panic!("coord {}", $coord_index);
}
}
};
($type_name: ident, $coord_name: ident, $coord_index: expr, $($extra_coord_name: ident, $extra_coord_index: expr),+) => {
impl_point_accessors!($type_name, $coord_name, $coord_index);
impl_point_accessors!($type_name, $($extra_coord_name, $extra_coord_index), +);
}
}
macro_rules! dimension_to_coord_pairs {
(1, then $cb:ident!($($cb_args:tt)*)) => {
$cb!($($cb_args)* x, 0);
};
(2, then $cb:ident!($($cb_args:tt)*)) => {
$cb!($($cb_args)* x, 0, y, 1);
};
(3, then $cb:ident!($($cb_args:tt)*)) => {
$cb!($($cb_args)* x, 0, y, 1, z, 2);
};
(4, then $cb:ident!($($cb_args:tt)*)) => {
$cb!($($cb_args)* x, 0, y, 1, z, 2, w, 3);
};
}
struct Point<T>(Vec<T>);
dimension_to_coord_pairs!(2, then impl_point_accessors!(Point,));

What’s the difference between ‘x: () => Int’ and ‘x: => Int’

When using these in a function parameters description, they have different effects.
Only the latter form can accept multi-line operations like
{
println(“hello”)
println(“world”)
1
}
However, the former can’t.
I know ‘()’ means “no parameters”, right? But what ‘’ means in ‘=>Int’?
Here's the whole story.
Define a function
def func(x: =>Int)= {
println(x)
}
Invoke it
func {
println("hello")
println("world")
1
}
We will get
hello
world
1
However, if we define the function as
def func(x: ()=>Int)= {
println(x)
}
Invoke it using the former code, we will get
error: type mismatch;
found : Int(1)
required: () => Int
1
^
So, what's the difference between ‘x: () => Int’ and ‘x: => Int’?
Behaviors Of Call By Name Evaluation VS Higher-Order Functions
Call By Name Evaluation:
In case of call by name, the expression is evaluated before the function is invoked.
Example:
def func(x: =>Int): Int= {
x
}
func(10 + 2)
Higher-Order Functions:
In case of HOF, the function is passed and its result are computed when the function is invoked.
Example:
def func(x: () =>Int): () => Int = {
x
}
func(() => 10 + 2)
Note: Check the return types for more clarity.
Essentially, there is no difference. Both represent 0-arity functions which result in an Int value.
However, there is a big difference in how they are used. One, x: => Int, is used for a call-by-name parameter in a method and is invoked simply by referencing an Int. The compiler does the rest.
The other form, x: () => Int, is used for a method parameter where you really want to pass in a 0-arity function. But when you use x within your method, you must actually give it parentheses. And, of course, when you invoke the method, you need to pass in a function (or partially-applied method), not an Int.
Here's an example:
def and(a: Boolean, b: () => Boolean): Boolean = if (a) b() else false
def not(): Boolean = false
println(and(true, not))

Can macros match against constant arguments instead of literals?

Given the macro matching example, this shows how macros can match an argument.
I've made very minor changes here to use numbers:
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(1 => 3);
}
Works, printing: mode Y: 3
However I would like to use a constant as an argument, can this be made to work:
const CONST: usize = 1;
macro_rules! foo {
(0 => $e:expr) => (println!("mode X: {}", $e));
(1 => $e:expr) => (println!("mode Y: {}", $e));
}
fn main() {
foo!(CONST => 3);
}
Is this possible in Rust?
Note, using a regular match statement isn't usable for me, since in my code each branch resolves to different types, giving an error.
So I'm specifically interested to know if a constant can be passed to a macro.
No.
Macros operate on the Abstract Syntax Tree, so they reason at the syntactic level: they reason about tokens and their spelling.
For example:
fn main() {
let v = 3;
}
In this case, the AST will look something like:
fn main
\_ let-binding v
\_ literal 3
If you ask a macro whether v is 3, it will look at you funny, and wonder why you would try comparing a variable name and a literal.
I'm fairly sure the answer is "no"; at macro expansion time all you have are token trees - expansion happens before evaluation, or even type inference/checking.
const CONST: usize = 0;
macro_rules! foo {
($i:ident => $e:expr) => {
if $i == 0 {
println!("mode X: {}", $e);
} else if $i == 1 {
println!("mode Y: {}", $e);
}
};
}
fn main() {
foo!(CONST => 3);
}
If you want use identifier in macro it needs to be ident tag and you can use if, else if blocks instead of match.

Scala, passing a locally defined function into a list?

I have not understood the following code snippet why afterDelay(0) {...}, a locally defined function can be stored into agenda? Can someone help me understand the afterDelay(0) {...} in the run function?
abstract class Simulation {
type Action = () => Unit
case class WorkItem(time: Int, action: Action)
private var curtime = 0
def currentTime: Int = curtime
private var agenda: List[WorkItem] = List()
private def insert(ag: List[WorkItem], item: WorkItem): List[WorkItem] = {
if (ag.isEmpty || item.time < ag.head.time) item :: ag
else ag.head :: insert(ag.tail, item)
}
def afterDelay(delay: Int)(block: => Unit) {
val item = WorkItem(currentTime + delay, () => block)
agenda = insert(agenda, item)
}
private def next() {
(agenda: #unchecked) match {
case item :: rest =>
agenda = rest
curtime = item.time
item.action()
}
}
def run() {
afterDelay(0) {
println("*** simulation started, time = "+
currentTime +" ***")
}
while (!agenda.isEmpty) next()
}
}
afterDelay(0) {
println(...)
}
Is equivalent to the following:
afterDelay(0)({
println(...)
})
The function afterDelay is invoked a new WorkItem (item) is added to the list, not the function itself. The parameter block: => Unit is a "By-Name Parameter" (see the Scala Language Specification section 4.6.1): the expression used as the argument is implicitly converted into a "parameterless method" (without being evaluated first) that will be invoked whenever the variable inside the method is accessed (no () required).
In this case that is when the function resulting from () => block is invoked: it is invoked at item.action() which occurs at some point after the new WorkItem is added to the list (and afterDelay returns).
If it was written as (taking in a function paramater, not a by-name/thunk):
def afterDelay(delay: Int)(block: () => Unit) { // take a function
// new function will invoke function named by "block" when invoked ...
val item = WorkItem(..., () => block())
// or skip wrapping the function in a function ...
// val item = WorkItem(..., block)
...
}
Then it would need to be invoked passing in a function:
afterDelay(0)(() => { // need a function
println(...)
})
Or, alternative syntax, still a function of () => Unit, but the outside parenthesis can be avoided:
afterDelay(0) { () => // need a function
println(...)
}
Extract from the SLS, 4.6.1 By-Name Parameters:
The type of a value parameter may be prefixed by =>, e.g. x: => T. The type of such a parameter is then the parameterless method type => T. This indicates that the corresponding argument is not evaluated at the point of function application, but instead is evaluated at each use within the function. That is, the argument is evaluated using call-by-name.
You defined afterDelay as a curried function. This means it has two parameter lists. In scala you can replace the brackets surounding the parameterlist (...) with {...}. In the second parameterlist you are using a "call-by-name" parameter. Those parameters are evaluated everytime again you use them in your function. A good Example is here.
"Call-by-name" parameters are often used to define your own controlling structure.
def do(until: Int)(body: => Unit) {
body
if (until > 1) do(until - 1)(body)
}
do(0)(println("test"))
do(5)(println("test2"))
This is an example for a do until. It will print once test and five times test2.