Closed mominul closed 1 year ago
note if you want if const {} else {}
syntax, we need to make it invalid to use const {...}
blocks in if
without parenthesis, so we need to adjust the syntax before const {}
blocks stabilize. https://github.com/rust-lang/rust/issues/76001
A ?
prefix could also be used, so ?const
actually, now that I think of it, if async { foo(0) } else {}
conflicts with already existing stable syntax if async { foo(0) }.await else {}
because of Rust's must-be-able-to-parse-with-3-token-lookahead rule.
@programmerjake the parser ambiguity is indeed why we decided against proposing that exact syntax, even if we think it would have been ideal if it could’ve worked.
——-
@mominul if you have the time, I’d be interested in reading how believe your proposal would compare to the effect/.do
system we touched on in our blog post. We intentionally didn’t get into all details, such as constraining effects, but I’d still be interested in learning more about what your proposal does differently and whether there’s anything you believe we may have overlooked.
one other possible syntax option is if effect async {} else if effect const {} else {}
If you really want to get Rust-y, then you could
match effect {
async => ...
const => ...
_ => ...
}
If you really want to get Rust-y, then you could
match effect { async => ... const => ... _ => ... }
that only works if effect
is a keyword (rather than just a soft-keyword), otherwise it's ambiguous with match
-ing on a variable named effect
. Also, match
has the drawback of not easily expressing stuff like async & const
Yeah I wouldn’t mind if effect const
tbh. I believe this could even work with the is
keyword proposal so it could become if effect is const {}
— which could open up a path towards of the match syntax too.
We’ll have to see what makes most sense for this. One option is to go with a free function first, and bring it into syntax later. Or perhaps use a macro first. We can probably try some things out to figure out what would work best.
imho a free function is a bad idea since it would either have to be compiler magic (not a normal expression) or would prevent using things that don't type check for both try
/!try
(and other things that change types such as async
):
e.g.
the following won't work with just a normal compiler intrinsic:
pub ~try fn func(f: impl ~try Fn() -> String) -> String {
if is_try() {
let result = f();
result.is_ok(); // type error: String doesn't have an is_ok() method
// this is because everything in `if false {}` still needs to type check
todo!()
} else {
f()
}
}
though otoh something like const_eval_select
can work, though is un-ergonomic
I mean, if folks strongly prefer the const-eval-select api as an interim solution over a free function, that's fine by me.
I mostly care that we have something people can use straight away, and we can always figure out a good ergonomic solution later on.
if I had to pick between const_eval_select
and is_const()
, I'd pick const_eval_select
because is_const()
just plain doesn't work due to the false branch of the if
still needing to type check and resolve names and stuff.
If you really want to get Rust-y, then you could
match effect { async => ... const => ... _ => ... }
This is actually something similar to what I proposed on the Zulip thread, haha
fn foo<T>(t: T) where T: Async + Const + Mut -> U { ... }
// with matching on the type level Async, Const, Mut and extracting what context the function is running, whether async, const, etc
match CONTEXT {
Const => ...,
Async => ...,
...
}
This isn't exactly feasible it seems but at least for the function syntax, I mentioned on #10 (my comment which @mominul kindly referenced), having an explicit effects
clause similar to where
would be interesting:
fn foo<F, T>(closure: F) -> Option<T>
where
F: FnMut(&T) -> bool,
effect
const if F: const,
?async,
{ /* ... */ }
which extends this current proposal with conditional contexts on whether a function would be const
, async
, etc.
@yoshuawuyts in comparison to the effect
/.do
notation, I think it just looks cleaner to have the function signature be more clearly visible instead of having multiple ?
s/?effect
s everywhere. If I want to know that there is an effect, I can look further into the function, just as how where
clauses work today, being afterwards/below the function signature.
It is also nice to specify exactly which effects are used (async
, const
and possibly future user-defined effects even) but again not run into the soup of ?
s before and inside the function signature. I think in essence this effect
syntax proposal would solve both the ?const ?async !panic !unwind ... fn function(...)
problem of being hard to parse, as well as not have the need to have a ?effect
keyword that simply works over all such effects. But then again you mentioned you hadn't shown the details yet of conditional effects so I would have to wait and see what the syntax for that looks like.
Basically, I think the ?
prefix looks odd in current Rust and I'd rather ?
were used sparingly or even not at all if possible.
I'm in the process of creating an overview of some of the alternative syntax designs, basing it on the following snippet:
/// A trimmed-down version of the `std::Iterator` trait.
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
fn size_hint(&self) -> (usize, Option<usize>);
}
/// An adaptation of `Iterator::find` to a free-function
pub fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T>
where
I: Iterator<Item = T> + Sized,
P: FnMut(&T) -> bool;
@mominul I'd be interested in the following two translations of this snippet to your design:
async
.async
.effect/.do
semantics).If you believe more variants would be helpful to include as well, please feel free to. Thank you!
edit: I've shared an example of a design overview here: https://github.com/rust-lang/keyword-generics-initiative/issues/10#issuecomment-1445253811
@yoshuawuyts I think we can map the design we're discussing here like the following:
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>;
fn size_hint(&self) -> (usize, Option<usize>);
}
pub fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T>
where
I: Iterator<Item = T> + Sized,
P: FnMut(&T) -> bool;
pub trait Iterator {
type Item;
async fn next(&mut self) -> Option<Self::Item>;
fn size_hint(&self) -> (usize, Option<usize>);
}
pub async fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T>
where
I: Iterator<Item = T> + Sized,
P: async FnMut(&T) -> bool;
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>
effect async;
fn size_hint(&self) -> (usize, Option<usize>);
}
pub fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T>
where
I: Iterator<Item = T> + Sized,
P: FnMut(&T) -> bool effect async;
effect
async
pub trait Iterator {
type Item;
fn next(&mut self) -> Option<Self::Item>
effect async, const;
fn size_hint(&self) -> (usize, Option<usize>);
}
pub fn find<I, T, P>(iter: &mut I, predicate: P) -> Option<T>
where
I: Iterator<Item = T> + Sized,
P: FnMut(&T) -> bool effect async, const;
effect
async, const
@mominul Instead of tracking design proposals in a GitHub thread, I figured it might actually be better if we start checking them in. Can I ask you to create a branch based off this template and file a PR containing your design? That should make it easier to look up the design later on. If it's easier if I do it just let me know. Thank you!
Okay, I'll do it!
@yoshuawuyts I have filled a PR containing the design #23
Motivation
As per the progress report, the
keyword-generics
initiative is heading towards an initial vision that will lead syntax usage like:From my perspective and think many will agree that a function declaration like this:
or an extreme example:
is really noisy and cumbersome to digest and to have a feel at. It would make the syntax surface of Rust more complicated and make the learning curve steeper.
Details
Personally, I consider that renaming
~
and@
pointers toBox
,Rc
, andArc
was a great decision made by the Rust team. From my perspective, sigils may shorten the code, but it introduces the need for extra attention while reading the code and makes it noisier in most cases.I personally really applaud the Rust team for starting this initiative, code duplication because of function coloring is a deep issue and we need to take it down! But currently proposed syntaxes(
?async
,fn<?async>
) don't feel like we are heading toward the right syntax to begin with.Using the
effect
clauseI want to propose the usage of the
effect
clause to achieve operation genericity, for example:So in a nutshell, a function declaration with an
effect
clause is a special type of function that may or may not exhibit async or const behavior(effect) and it depends on the context of the function being called from and we can execute a different piece of code according to the context from the function was called from too (like theconst_eval_select
, resolves #11):Acknowledgment
I have been greatly influenced by the comments in #10, especially of @jssblck and @satvikpendem
It's my first time writing a proposal, so I might be missing stuff or being wrong, but I wanted my concerns to be heard and start a discussion about an alternative approach.
Thanks!