vvvv / VL-Language

The official repo for the design of the VL programming language
31 stars 0 forks source link

[Proposal] Custom Region DSL #52

Open gregsn opened 2 years ago

gregsn commented 2 years ago

targets Quest: #51

Current situation

The current custom region API allows regions with BCPs (border control points). However, some features are lacking:

Stance

We should try everything to have this one unified API. It promises a unified and fluent patching experience and at the same time enables anyone to define new regions. However, the way it is built only knows a one-way information flow: From the application-side to the definition-side of the region. It's really just able to query into the users' use of the region: "give me the BCPs and I'll try to make use of them".

What we need is to enhance this API in a way that the definition-side has a say. This is true for every single requested feature above. And also for all of those features, we need to have a say at "compile-time":

So how do we talk to the compiler? We only have an API that allows doing calls on runtime.

Solution

This proposal is saying: the requested feature set is big enough to NOT go for attributes as this would be too messy. How about defining a little domain-specific language? A language only for defining the intrinsics of the region.

As a region designer, you already create a pin and type it as a ICustomRegion in order to establish it. How about being able to configure this pin and put a small code snippet that declares how the region behaves at compile time?

Examples

Let's say you want to define a reactive loop, that allows working with one input splicer and many output splicers:

IsLoopLike
InSplicer<T>: Observable<T> -> T
OutSplicer<T>: T -> Observable<T>

This is saying how the inside and the outside data types of the splicer BCPs relate. It might also say that the region designer doesn't care for accumulators. Let's say the patch editor only knows If-like regions, Loop-like regions, and others (in order to not overdesign everything). In that case, by specifying how splicers work, you'd already end up with a loop-like region, which also comes with accumulator pins. But maybe that's fine:

Let's say you want to define a GPU-loop:

IsLoopLike
InSplicer<T>: GPUValue<T> -> GPUValue<T>
OutSplicer<T>: GPUValue<T> -> GPUValue<T>
Accumulator<T>: GPUValue<T>
Input Index: GPUValue<Integer32>

In this loop, the input and output types on splicers are the same. It also comes with an Index pin in the patch.

In general

The instruction set can be fine-tuned and extended. We don't need to attach several attributes to one code element.

Statements that might get obsolete at some point:

Optional:

gregsn commented 2 years ago

I forgot to mention: Feeding data to the patch inputs (like Index) or retrieving data from a patch output would just work along the same lines as with the BCPs, which currently is done via the ICustomRegionPatch.Update call.

    public ICustomRegionPatch Update(
        IReadOnlyList<object> inputs, IReadOnlyList<(string name, object value)> patchInputs, IReadOnlyList<object> incomingLinks,
        out Spread<object> outputs, out IReadOnlyList<(string name, object value)> patchOutputs);

However, like discussed in #54 the design of this interface depends on whether or not the multi-moment idea shall be tackled as well.

What's interesting with the approach here is that BCPs and patchPins are treated in the same fashion. The other approach of defining moments via operations of an interface looks like patch pins are treated differently from BCPs.