1 stable release
1.0.0 | Jan 6, 2024 |
---|
#1376 in Procedural macros
9KB
51 lines
!
-notation, brought to Rust
This crate provides bang!
macro, similar to !
-notation in Idris2.
With it, the following expression:
bang!(!x + !y + z)
Gets desugared into
x.and_then(|x| {
y.and_then(|y| {
x + y + z
})
})
What problem does this solve?
Rust provides neat containers to make error-handling easy and convenient. However, sometimes one, like myself, can find themselves in a peculiar situation.
Suppose there is some function that produces an Option
:
fn some_func(x1: T1, x2: T2, x3: T3) -> Option<u32>
And you even have the arguments prepared to call the function.
However, there's a catch: the arguments were also produced in some way that made them wrapped in Option
.
So, we only have x1: Option<T1>
, x2: Option<T2>
and x3: Option<T3>
in our hands.
This, of course, prompts us to write something along the lines of:
x1.and_then(|x1| {
x2.and_then(|x2| {
x3.and_then(|x3| {
some_func(x1, x2, x3)
})
})
});
This looks horrible and requires us to write a lot of pointless symbols!
Luckily, programming language Idris2 (which I greatly adore) provides a nice solution to this problem.
It has a nice piece of syntactic sugar called !-notation.
In Idris, the expression prefixed with !
is lifted as high as possible and bound to a fresh name.
Then the original expression is replaced with this newly bound name.
Following the Idris documentation, it transforms the following expression
f !(g !(print y) !x)
into
do y' <- print y
x' <- x
g' <- g y' x'
f g'
This crate provides a procedural macro that performs a similar transformation in Rust. Simply import it:
use bang_notation::bang;
And then we could replace the unreadable mess we had originally written with a concise:
bang!(some_func(!x1, !x2, !x3))
It would get desugared to the chain of and_then
s we initially came up with.
What can you use this with?
In Idris, !
-notation can be used with arbitrary monads.
This Rust version aims to be as general as possible.
It is supposed to work with any type that provides and_then
method with a suitable signature.
If you found any example where this doesn't work, let me know!
Is this any better than ?
operator?
Yes! AFAIK, currently ?
operator works only with Option
and Result
types.
There is an experimental Try
trait that could be used to overload ?
operator,
but it provides semantics different to !
-notation in this crate.
Unlike ?
, !
-notation is applicable to arbitrary types that provide a suitable interface for and_then
.
As an example, you might want to take a look at list001
test that uses a custom-written List
monad.
In what order are values bound?
The expressions marked with !
are bound left-to-right, in order they are written in the source code.
In case of nested expressions marked with !
, the inner expressions are bound first.
After that, they are replaced with new names in the outer expression, and the outer expression is bound.
Dependencies
~220–660KB
~16K SLoC