Skip to content

Idea: Support for global substitutions, and for generating items once / starting a new akin context #1

@d4h0

Description

@d4h0

Hi @LyonSyonII,

First of all, thanks for creating akin. I like the approach quit a lot.

I'm wondering if global substitutions, and generating items only once can be added to akin.

An example describes the use case best:

Imagine we would like to create an extension trait for serde_json::Value that adds methods like into_xy methods that return owned values.

I'd create and implement a trait like this:

trait ValueExt {
    fn into_null(self) -> Result<(), Self>;
    fn into_bool(self) -> Result<bool, Self>;
    fn into_array(self) -> Result<Vec<Value>, Self>;

    // etc...
}

impl ValueExt for Value {
    fn into_null(self) -> Result<(), Self> { todo!()}
    fn into_bool(self) -> Result<bool, Self> { todo!()}
    fn into_array(self) -> Result<Vec<Value>, Self>  { todo!()}
    
    // etc...
}

Instead of writing all of this by hand, I'd like to use akin to generate the trait and the trait implementation:

akin::akin! {
    let &ident = [ null, bool, array, /* ... */ ];
    let &Type = [
        { () },
        { bool },
        { Vec<Value> },
        // ...
    ];

    trait JsonValueExt {
        fn into_~*ident(self) -> Result<*Type, Self>;
    }

    impl ValueExt for serde_json::Value {
        fn into_~*ident(self) -> Result<*Type, Self> { todo!() }
    }
}

But of course this doesn't work (JsonValueExt is defined several times).

I'm wondering if this use case somehow can be supported.

For example via something like this:

akin::akin! {
    // ...
    
    #[akin(context)]
    trait JsonValueExt {
        // A new `akin` context
        fn into_~*ident(self) -> Result<*Type, Self>;
    }

    // ...
}

#[akin(context)] would tell akin to generate the decorated item only once, and substitute within the item (similar to how $($ident)+ works for macro_rules macros). Something like akin_context! { /* new context */ } probably also would be useful, for cases where attributes can't be used.

My first idea for a name of the new attribute was inner, but I think that wouldn't be ideal, because it should be possible to substitute things within the trait definition itself (e.g. for generic traits). Maybe there is a better name than context, however.

Similarly, the duplicate crate has a nice feature to define global substitutions, which often come in handy.

I'm wondering if this can be supported via something like:

akin::akin! {
    // ...
    
    const &foo = { println!("Hello, world!") };

    *foo
    // ...
}

...which would expand to just one println!("Hello, world!").

Using const here would be perfect semantically, I believe.

These features would make akin useful in even more cases.

Currently, I have created an additional macro_rules macro to generate the trait definition (duplicating the &Type and &ident definitions), which works but is not really ideal.

Would it be possible to add these features?

Currently, I haven't learned how to use procedural macros, so I currently can't add these features myself. But if you don't want to add this yourself, but would accept a PR, I could do this when I learn procedural macros at some point (however, that would most likely be at least several months into the future, if not more).

Thanks again for creating akin!

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions