Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add some syntax sugar for observable models #4

Open
nadako opened this issue Dec 8, 2022 · 5 comments
Open

Add some syntax sugar for observable models #4

nadako opened this issue Dec 8, 2022 · 5 comments
Labels
enhancement New feature or request

Comments

@nadako
Copy link
Owner

nadako commented Dec 8, 2022

I'd like to add something like coconut.data. There's no macros in C# so we'll have to do some IL weaving magic, but I'm pretty sure it would be possible to have something like:

[Model]
class Data {
    [Observable] public string Name { get; set; } 
    [Observable] public string Greeting => $"Hello, {Name}!";
}

and generate something like this from that:

class Data {
    public string Name { get => _Name.Value; set { _Name.value = value; }}
    public string Greeting => _Greeting.Value;

    State<string> _Name = Observable.State(default);
    Observable<string> _Greeting = Observable.Auto(() => $"Hello, {Name}!");
}
@nadako
Copy link
Owner Author

nadako commented Dec 8, 2022

To do that, we need to have an assembly post-processing task made with Mono.Cecil, it would iterate over types, find [Model] classes, find [Observable] properties in it, remove their default backing fields, add backing observables and change the get/set methods to use that.

@nadako
Copy link
Owner Author

nadako commented Dec 8, 2022

We also need a MSBuild task to easily add this post-processing, and for Unity we need to hook into the CompilationPipeline.

@nadako nadako added the enhancement New feature or request label Dec 8, 2022
@nadako
Copy link
Owner Author

nadako commented Dec 9, 2022

I've played with this today and had some success, one thing that comes in mind is that we also want to expose underlying Observable and State objects for easy binding. In coconut.data this is easy because the macros generates observables fields with everything. With IL weaving this is harder, since it's a post-processing operation and we cannot add things that will be visible by the users of the model.

So far I have one idea how to implement it. Instead of the [Model] attribute, we'll have models implementing the special Model marker interface for which we'll have an extension method like this:

static class ModelExtensions
{
    static Observable<T> GetObservable<T>(this Model model, string fieldName) {
        return ((ModelInternal)model).GetObservable<T>(fieldName);
    }
}

So you can then do something like data.GetObservable<string>(nameof(Data.Name)).Bind(...), which is not very nice (not type-safe enough), but it's something.

Internally, we'd have another special interface like ModelInternal that actually defines per-type GetObservable<T> method, and then we'd implement this interface when doing the weaving with a switch or something.

If someone has better ideas how to do it, suggestions are welcome :)

@nadako
Copy link
Owner Author

nadako commented Dec 9, 2022

So you can then do something like data.GetObservable<string>(nameof(Data.Name)).Bind(...), which is not very nice (not type-safe enough), but it's something.

OK, for type-safety (and type inference!) we could also use the expression tree feature of C#, so the API would be something like var observable = GetObservable(() => player.Name).

@nadako
Copy link
Owner Author

nadako commented Dec 10, 2022

FYI I'm experimenting with it in the model branch (beware - code is bad).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant