Wouldn't the signature of fold
then be:
/// Fold over a function that may return the final value early.
fn fold<'ret, OutRet, R, F>(&mut self, init: R, f: F)
-> R, 'ret: OutRet
where
F : (FnMut<'f_ret>(A, Self::Item) -> R, 'f_ret: OutRet)
;
with the semantics of:
- nevermind the (currently ugly) syntax, just focus on the semantics:
// having,
enum MultiReturn<Inner, Outer = !, Break = !> {
InnerRet(Inner),
OuterRet(Outer),
Break(Break),
}
// and
#[lang = "..."] // for the `.inlined!()` / `in<'label> {}` sugar (see below)
trait Inlineable
:
FnOnce<(), Output = MultiReturn<Self::InnerRet, Self::OuterRet, Self::Break>>
{
type InnerRet;
type OuterRet;
type Break;
}
// then
fn... <'caller> (...) -> Inner, 'caller: return Outer $(, break Break )?
{
//* body */
}
// is sugar for:
fn... (...) -> impl Inlineable<Inner = Inner, Outer = Outer $(, Break = Break)?>
{
in<'caller> { // captures scope and creates state machine, much like `async`
/* body */
}
}
// Yielding the following usage:
iterator.fold(acc, in<'caller> |acc, x| {
if cond() {
return 'caller 0;
} else {
return foo;
}
}).inlined!()
// which desugars to:
match iterator.fold(...)() {
| MultiReturn::InnerRet(inner) => inner,
| MultiReturn::OuterRet(outer) => return outer,
| MultiReturn::Break(break_value) => break break_value, // valid outside a loop if unreachable (`break_value: !`)
}
After having written this, I realize that multi returns have always a scope / context, meaning that the name of the outer scope, e.g., 'caller
, plays no role, so we could imagine 'caller
becoming a keyword scope / label (feel free to bikeshed for another keyword name):
/// Fold over a function that may return the final value early.
#[inlineable(return = OutRet, break = Break)]
fn fold<OutRet, Break, Acc, F>(&mut self, init: Acc, f: F) -> Acc
where
F : FnMut<(Acc, Self::Item)>,
F::Output : Inlineable<InnerRet = Acc, OuterRet = OutRet, Break = Break>,
;
and then we could have inline { ... }
scopes much like async { ... }
scopes:
#[inlineable(...)]
fn... (...) -> Inner
{
//* body */
}
// is sugar for:
fn... (...) -> impl Inlineable<Inner = Inner, ...>
{
inlineable { // captures scope and creates state machine, much like `async`
/* body */
}
}
// Yielding the following usage:
iterator.fold(acc, |acc, x| inlineable {
if cond() {
return 'caller 0;
} else {
return foo;
}
}).inlined!()
except for the postfix macro, I may be able to create this week-end a working PoC using (procedural) macros