How to use variadic macros to call nested constructors? - macros

I'm trying to create a macro in Rust that lets me write
make_list!(1, 2, 3)
instead of
Node::new(1, Node::new(2, Node::new(3, None)))
which should work for an arbitrary number of "parameters" including zero. This is what I have so far:
macro_rules! make_list(
() => (
None
);
( $x:expr, $( $more:expr ),* ) => (
Node::new($x, make_list!( $( $more ),* ))
)
);
but I get the following error:
error: unexpected end of macro invocation
--> src/main.rs:19:42
|
19 | Node::new($x, make_list!( $( $more ),* ))
| ^^^^^
I can't make much sense of this. From what I can tell, it should work. What did I do wrong?
The complete code:
type List<T> = Option<Box<Node<T>>>;
struct Node<T> {
value: T,
tail: List<T>,
}
impl<T> Node<T> {
fn new(val: T, tai: List<T>) -> List<T> {
Some(Box::new(Node::<T> {
value: val,
tail: tai,
}))
}
}
macro_rules! make_list(
() => (
None
);
( $x:expr, $( $more:expr ),* ) => (
Node::new($x, make_list!( $( $more ),* ))
)
);
fn main() {
let _list: List<i32> = make_list!(1, 2, 3, 4, 5, 6, 7, 8, 9);
}

Expanding on the error: you get down to the case where there is only one value, and so it writes make_list!(1). However, there is no rule that will match that, for the second rule, after consuming the expression x, wants a comma, which is not provided.
So you need to make it so that it will work for make_list!(1) and not just (in fact, just not) make_list!(1,). To achieve this, get the comma inside the repeating part, like this:
macro_rules! make_list(
() => (
None
);
( $x:expr $( , $more:expr )* ) => (
Node::new($x, make_list!( $( $more ),* ))
)
);
Bonus: you can write make_list![1, 2, 3] instead of make_list!(1, 2, 3) if you want.

As noted by #chris-morgan's answer, expanding the single argument case isn't accounted for.
So you can either include comma in the expansion, or add a single case in the macro:
Example of both, single argument:
macro_rules! make_list {
() => (
None
);
($x:expr) => (
Node::new($x, None)
);
($x:expr, $($more:expr),+) => (
Node::new($x, make_list!($($more),*))
);
}
Including the comma in the expansion:
macro_rules! make_list {
() => (
None
);
($x:expr $(, $more:expr)*) => (
Node::new($x, make_list!($($more),*))
);
}
Here is a fully working example based on the question and updated for Rust 1.14:
type List<T> = Option<Box<Node<T>>>;
#[derive(Debug)]
struct Node<T> {
value: T,
tail: List<T>
}
impl<T> Node<T> {
fn new(val: T, tai: List<T>) -> List<T> {
Some(Box::new(Node::<T> { value: val, tail: tai }))
}
}
macro_rules! make_list {
() => (
None
);
($x:expr $(, $more:expr)*) => (
Node::new($x, make_list!($($more),*))
);
}
fn main() {
let list: List<i64> = make_list!();
println!("{:?}", list);
let list: List<i64> = make_list!(1);
println!("{:?}", list);
let list: List<i64> = make_list!(1,2,3,4,5,6,7,8,9);
println!("{:?}", list);
}

Related

How to create custom operator from a pipe of operators in IxJS?

In rxjs6, we can create an operator from a pipe of operators.
import { pipe } from 'rxjs';
function doSomething() {
return pipe(
map(...),
flatMap(...),
);
}
$.pipe(
map(...),
doSomething(),
flatMap(...),
)
Is there a way to create an operator like this in IxJS?
You can combine operators manually:
import { IterableX as Iterable } from 'ix/iterable';
import { map, filter } from 'ix/iterable/pipe/index';
function customOperator() {
return source$ => map(x => x * x)(
filter(x => x % 2 === 0)
(source$)
);
}
const results = Iterable.of(1, 2, 3, 4).pipe(
customOperator()
).forEach(x => console.log(`Next ${x}`));
Or write your own pipe implementation:
const pipe = (...fns) =>
source$ => fns.reduce(
(acc, fn) => fn(acc),
source$
);
function customOperator() {
return pipe(
filter(x => x % 2 === 0),
map(x => x * x)
)
}

Calling functions with different numbers of arguments in Rust 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

Why underline not work in some scenario of monad?

Underline _ cannot work for some scenario in following code:
case class Item
(
para: Int
)
val a = List(1, 2)
val b = List(Item(1), Item(2))
a foreach {
perA => println(perA * 2) // Line A: ok
}
a foreach {
println(_) // Line B: ok
}
a foreach {
println(_ * 2) // Line C: not ok
}
b foreach {
perB => println(perB.para) // line D: ok
}
b foreach {
println(_.para) // line E: not ok
}
Could someone explain for me about line C & line E, thanks.
TL;DR: the argument of the lambda can be inserted at an unexpected position. Instead of taking
a foreach { println(_.blah) }
and building
a foreach { x => println(x.blah) }
out of it, the compiler instead builds
a foreach { println( x => x.blah ) }
and then fails to derive the type for the argument x.
A) This is the most explicit way to write down the lambda, the only way to make it clearer would be to add a type:
a foreach { perA => println(perA * 2) }
is the same as
a foreach { (x: Int) => println(x * 2) }
This should obviously work.
B) This works because it's one way to write down the function automatically generated from println. It is equivalent to any of the six variants below:
a foreach { (x: Int) => println(x) }
a foreach { x => println(x) }
a foreach { println(_) }
a foreach { println _ }
a foreach { println }
a foreach println
C) Because of the * 2, this here
a foreach { println(_ * 2) }
can no longer be considered just println(_). Instead, it is interpreted as
a foreach { println( { (x: ??!) => x * 2 } ) }
and since println takes no function-valued arguments, it cannot determine what the type of x is supposed to be, and exits with an error.
D) Is essentially the same as A, it works, I hope it's clear.
E) Is a variation of C, but this time, the type-checker isn't looking for something with method *(i: Int), but instead it's looking for something with a member para.
This here:
b foreach { println(_.para) }
is again interpreted as a foreach with a function which ignores elements of b and returns the constant value of the expression println(_.para),
that is:
b foreach { println( { (x: ??!) => x.para } ) }
Again, the inner expression println( { (x: ??!) => x.para } ) does not make any sense, because println does not expect function-valued arguments (it can handle Any, but it's not enough to derive the type of x).

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.

How to write a macro in Rust to match any element in a set?

In C, I'm used to having:
if (ELEM(value, a, b, c)) { ... }
which is a macro with a variable number of arguments to avoid typing out
if (value == a || value == b || value == c) { ... }
A C example can be seen in Varargs `ELEM` macro for use with C.
Is this possible in Rust? I assume it would use match. If so, how would variadic arguments be used to achieve this?
macro_rules! cmp {
// Hack for Rust v1.11 and prior.
(#as_expr $e:expr) => { $e };
($lhs:expr, $cmp:tt any $($rhss:expr),*) => {
// We do this to bind `$lhs` to a name so we don't evaluate it multiple
// times. Use a leading underscore to avoid an unused variable warning
// in the degenerate case of no `rhs`s.
match $lhs { _lhs => {
false || $(
cmp!(#as_expr _lhs $cmp $rhss)
) || *
// ^- this is used as a *separator* between terms
}}
};
// Same, but for "all".
($lhs:expr, $cmp:tt all $($rhss:expr),*) => {
match $lhs { _lhs => {
true && $( cmp!(#as_expr _lhs $cmp $rhss) ) && *
}}
};
}
fn main() {
let value = 2;
if cmp!(value, == any 1, 2, 3) {
println!("true! value: {:?}", value);
}
if cmp!(value*2, != all 5, 7, 1<<7 - 1) {
println!("true! value: {:?}", value);
}
}
First off, if your a, b, and c are concrete values, you can just use match:
fn main() {
let x = 42;
match x {
1 | 2 | 3 => println!("foo"),
42 => println!("bar"),
_ => println!("nope"),
}
}
If you want to match on variables you need to write the match arms like this:
match x {
x if x == a || x == b || x == c => println!("foo"),
42 => println!("bar"),
_ => println!("nope"),
}
…which is basically what you want to avoid.
But: A pretty direct translation of your C macro is also possible!
macro_rules! elem {
($val:expr, $($var:expr),*) => {
$($val == $var)||*
}
}
fn main() {
let y = 42;
let x = 42;
if elem!(x, 1, 3, y) {
println!("{}", x);
}
}
I'm partial to writing this without a macro, taking advantage of contains on arrays.
fn main() {
if [1, 2, 3, 4].contains(&4) {
println!("OK");
}
}
It's hard to predict what will happen to this when optimized, but if absolute performance is a goal you'd do well to benchmark each approach.
Yes this is possible, the following macro expands to do each check.
macro_rules! elem {
($n:expr, $( $hs:expr ),*) => ($( $n == $hs )||* );
}
fn main() {
if elem!(4, 1, 2, 3, 4) {
println!("OK");
}
}
Thanks to #vfs on #rust in IRC.