Channels with CastAs: Bug, feature or question?

Came across something fishy, that is possibly a bug, question or a missing feature:

I have two data types, that both implement same interface, i want to cast from Channel<MyInterface> to Channel<MyDataType>. So as soon as i connect both channels together using Switch or Cons i can’t cast that channel back to the ones i want using TypeSwitch, or even CastAs. However if i strongly type my Channels they suddenly start to work.

See the example patch for details… CastAsWithChannels.vl (30.6 KB)

3 Likes

Addition to “what seems to be working”

CastAsWithChannelsFix.vl (46.6 KB)

1 Like

Sorry for the late reply.

I guess the main question is what you want to achieve.
If you want to have one channel, that is able to sometimes hold AAAA and after that BBBB, your channel should be of type ICCCC or Object.

You then can react as you showed inside the ForEach (Channel) and test for the item that got pushed.

I guess that this already is what you needed in the end, but I am lacking the big picture of your usecase.

Types, Subtypes and assignability

On a more abstract level, without knowing the context:

A Channel<ICCCC> is not a channel Channel<AAAA> and the other way around a Channel<AAAA> is not a subtype of Channel<ICCCC>. That’s why the TypeSwitch and the CastAs are not triggering.

But why is that? Let’s say you would be able to cast a Channel<ICCCC> to Channel<AAAA>. If somebody now pushes a BBBB into that Channel<ICCCC> you would end up with an inconsistency. The whole idea of static typing collapses, as the Channel<AAAA> now has a BBBB inside. That’s why .Net doesn’t allow it. The whole topic is called variance. A channel cannot be covariant, as a channel offers read and write access.

In other words a Channel<AAAA> is a Channel (Ungeneric) and a Object. And as a bonus it is also can be cast to Channel<Object>. We tricked .Net a bit to offer that.

But I guess this abstract level doesn’t help us too much.

Just be cautious whenever having a generic type M<T>.

  • covariant types like IReadOnlyList<out T>
    • A IReadOnlyList<AAAA> is always a IReadOnlyList<ICCCC>.
    • A IReadOnlyList<ICCCC> can be tested for IReadOnlyList<AAAA> which is true when the object got created that way (e.g. Spread<AAAA>).
  • types with read write access are just plain imcompatible, no matter whether AAAA and ICCCC are related or not.

Your actual question probably is about design patterns for channels and subtypes.

I would go for a Channel<ICCCC> and then try OfType [Reactive] if I am only interested in certain items.

3 Likes

The actual use case was something like use RegisterService to emit different types of objects including Channels based on channel it’s received. As I noticed in this case you would want to annotate downstream more then upstream… I think what we wanted is to create an model and convert it to some sort of controller but we wanted to still keep intact original model connection…

@gregsn

The use case was purely experimental as part of writing the software architecture. The pattern was chosen so that there is a model, its representation, and some runtime.

Further, we went in such a way that changes of model parts in the view would be reflected in the whole tree of model parts. Roughly speaking, if there is an object “light projector”, then changes in it would automatically notify the model about the change. This is well achieved through channels, generally a powerful thing. But suddenly it turned out that “light projector” can have some other relatives. And then the question arose, what if we try channels with an interface? And then this behavior was found. There was service registration, filtering, and a lot of other stuff involved, but once the mess was untangled, it became clear what was going on.

To be honest, a lot of time has passed, and it’s hard to reproduce the idea we tried back then. But I remember that new objects got channels to their constructors through service registration. So we wanted to organize communication between objects within an entity (model) without creating an engine of these messages, but only by filtering which objects these messages were intended for. I can explain it a bit confusingly, but I tried.

If you are really interested, we can try to reproduce that situation.