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

[Proposal] Custom region API using attributes #53

Open
azeno opened this issue Feb 3, 2022 · 2 comments
Open

[Proposal] Custom region API using attributes #53

azeno opened this issue Feb 3, 2022 · 2 comments
Labels

Comments

@azeno
Copy link
Member

azeno commented Feb 3, 2022

The proposal addresses quest #51. It's similar to #52 but instead of a DSL it uses an attribute one would add to the input pin taking the ICustomRegion.

  public class RegionAttribute : Attribute
  {
      public bool ShowAsNode { get; set; } // This flag is already in the UI

      public Type Accumulator { get; set; }

      public (Type inner, Type outer) InputSplicer { get; set; }

      public (Type inner, Type outer) OutputSplicer { get; set; }
  }

By predefining a set of type parameter marker classes (class T : Marker, class T1 : Marker, ...) we can define the type constraints of the inner and outer parts of a splicer, for example:

Region(
  Accumulator = typeof(T),
  InputSplicer = (inner: typeof(T), outer: typeof(IObservable<T>)),
  OutputSplicer = (inner: typeof(T), outer: typeof(IObservable<T>)))

or the GPU loop example (no splicers)

Region(Accumulator = typeof(GpuValue<T>))

The availble in- and outputs inside the region (as well as what moments would be available) could be modelled through an interface added by the region designer and implemented by the system.

If we'd define the ICustomRegion interface as

  public interface ICustomRegion<TUserPatch> where TUserPatch : ICustomRegionPatch
  {
      TUserPatch CreateRegionPatch(NodeContext Context, IReadOnlyList<object> initialInputs, out Spread<object> initialOutputs);
...
  }

  public interface ICustomRegionPatch
  {
      IReadOnlyList<object> Inputs { set; }
      IReadOnlyList<object> IncomingLinks { set; }
      Spread<object> Outputs { get; }
  }

The region designer could annotate the pin with say ICustomRegion<ITryCatchUserPatch> where ITryCatchUserPatch is defined by the designer with say

    // Will get implemented by the compiler
    interface ITryCatchUserPatch : ICustomRegionPatch
    {
        void Try();
        void Catch(Exception exception);
    }

extactly defining how the inner part of a region looks like.

Another example for a GPU loop could look like this

    // Will get implemented by the compiler
    interface IGpuLoopPatch : ICustomRegionPatch
    {
        void Update(GpuValue<int> iteration);
    }
@azeno azeno added the proposal label Feb 3, 2022
@gregsn
Copy link
Member

gregsn commented Feb 3, 2022

Wow! Nice!
It looks to me that we should break up the discussion into 2 parts:

Attributes

I see this as an alternative to the DSL approach. Or in other words: The Multi-moment idea should also be "compatible" with the DSL idea (the Input Index statement however wouldn't be necessary in that case).

Detail question: Where would the marker classes need to be declared so that I can use them in my Attribute?

Multi-moments

This is a very strong concept! Being able to define TryCatch or IfElse or even an enum Switch would be awesome.

Output BCPs

I guess the region designer would like to define which moments are allowed to write into the BCPs. In both IfElse and TryCatch it would be great to allow links in every moment into the output BCPs.

However, it may be that the region works in a way that writing into the BCPs doesn't make sense as they get overwritten by another moment invocation. E.g. in the most trivial Do region, it doesn't make sense to write into a BCP on Create and on Update, as the value is written on Update will always win. Maybe there are more examples. And maybe it depends on the moment? Do we have to reason about cases where the region designer might want to allow the user to write certain BCPs in one moment and others in another? This might not affect the API, but just the internals of the compiler, which would only instruct certain values to be written back when implementing the interface defined by the user.

@azeno
Copy link
Member Author

azeno commented Feb 3, 2022

They would be declared at a rather central spot - VL.Core.Markers? (for reference, saw that trick the first time here)

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

No branches or pull requests

2 participants