rust-lang / keyword-generics-initiative

Public repository for the Rust keyword generics initiative
https://rust-lang.github.io/keyword-generics-initiative/
Other
95 stars 11 forks source link

Possibility of inferring all functions as `maybe keyword` #60

Open ewoolsey opened 2 months ago

ewoolsey commented 2 months ago

One common complaint I see about the keyword-generics-innitiative is that the syntax can be quite verbose. The core idea I have is to essentially treat all unmarked keyword functions as maybe keyword. A solution I've been exploring in my head looks something like this:

/// Not awaiting or otherwise using the future for this function
/// would result in a warning
async fn foo_async() {}

/// Can optionally be called in any context and can optionally be awaited
/// Notice that all regular functions would fall into this category
/// Hopefully no need for additional syntax here?
fn foo_maybe_async() {
   // Perhaps #[cfg(async)] makes more sense than `if ?async`
   if ?async {
       // Do some async stuff
   } else {
       // Do some sync stuff
   }
}

Awaiting a function which doesn't contain an `?async' block/section would result in a warning. Conversely, not awaiting a function which does contain an async block/section while inside an async context would also emit a warning. I imagine this style of implementation should be fully backwards compatible.

Perhaps there's something I'm missing here, but declaring a function signature like

?async fn maybe_async_foo(){}

or

#[maybe(async)]
fn maybe_async_bar(){}

seems superfluous.

Currently, the compiler can tell if a const fn is valid. So it would seem to me that it should also be able to validate if calling unmarked (maybe const) functions from within a const function is valid. I'll give an example:

This example would compile because the compiler could check that bar_maybe_const has a valid const implementation

/// Notice we are marking explicitly as const
const fn foo_const() -> usize {
    bar_maybe_const()
}

/// Notice we are leaving unmarked (maybe const)
fn bar_maybe_const() -> usize {
    if ?const {
        return 1
    } else {
        do_something_at_runtime()
    }
}

This example would error because the compiler could check that bar_maybe_const has no valid const implementation

/// Notice we are marking explicitly as const
const fn foo_const() -> usize {
    bar_maybe_const()
}

/// Notice we are leaving unmarked (maybe const)
fn bar_maybe_const() -> usize {
    // we don't provide a valid const implementation
    do_something_at_runtime() <-- Error: fn do_something_at_runtime is not const
}

This is just something that's been spinning in my head for a while and there's a good chance I'm not seeing the whole picture here. But I'd love to get your thoughts/opinions on this.

clarfonthey commented 2 months ago

My first impression is that this is an API nightmare, since defaulting to maybe means now everything that doesn't want to be maybe has to be explicitly marked.

Like, you've just moved the verbosity somewhere else, and effectively required it to be used more often.

ewoolsey commented 2 months ago

now everything that doesn't want to be maybe has to be explicitly marked.

This isn't how I was envisioning it. You wouldn't have to declare non-async functions as the compiler would just infer them. For example:

fn foo(){}

would be automatically inferred to be !async fn foo() -> ()

async fn foo(){
    bar().await
}

would be automatically inferred to be async fn foo() -> ()

fn foo(){
    if ?async {
        bar().await
    }
}

would be automatically inferred to be ?async fn foo() -> ()

@clarfonthey The point of my proposal here is to virtually eliminate adding extra syntax to function signatures and let the compiler figure it out.

clarfonthey commented 2 months ago

Oh, that makes more sense, although this would be unprecedented in the language, at least for regular functions, since the reason why they require annotations is purely for API documentation purposes and not because they can't infer them.

I can definitely see the argument of doing this for closures, though. Although I'm not exactly sure how useful maybe-async closures are in general.