PurpleKingdomGames / tyrian

Elm-inspired Scala UI library.
https://tyrian.indigoengine.io/
MIT License
350 stars 26 forks source link

Add a way to map a `Sub` message to multiple ones #274

Open Iltotore opened 3 months ago

Iltotore commented 3 months ago

Currently, it is not possible to map a value produced by a Sub to multiple ones, which could be useful in many cases.

Here is my own use case: I am making a Scala wrapper for Pokemon Showdown's API. The messages sent by the server look like this:

>roomid
message1
message2
...

in a single text frame.

WebSocket#subscribe returns a Sub[F, WebSocketEvent] (excluding the mapping function asked in parameter) and I'd like to be able to turn this WebSocketEvent into multiple ServerMessage (from my API).

My first intuition was to use flatMap+Sub.combine but the former method does not exist and looking at the architecture of Sub, I'm not sure if it is possible to implement it cleanly.

Another approach would be to introduce a Sub[F, A]#mapMultiple (or similar name) taking a A => Iterable[B] and returning a Sub[F, B].

davesmith00000 commented 2 months ago

Hi!

Yes, so currently I guess what you'd need to do is make a Msg that contains a list of the sub messages and then on model update, either fire them off individually or process the batch.

the former method does not exist

Indeed, Cmd's and Sub's are not monadic.

Sub's making other subs sounds dubious. What you want - I think - is not more sub's but a way to return multiple messages from your sub? Have a read that right?

Iltotore commented 2 months ago

Yes, so currently I guess what you'd need to do is make a Msg that contains a list of the sub messages and then on model update, either fire them off individually or process the batch.

Yes this is my current workaround althrough I'd like this "implementation detail" to not leak.

a way to return multiple messages from your sub? Have a read that right?

Yes. A way to map a single message produced by a Sub to multiple ones.

In my use case, I have a Sub[F, WebSocketFrame] that I'd like to map to a Sub[F, ServerMessage] potentially returning multiple ServerMessage for one WebSocketFrame.

davesmith00000 commented 2 months ago

Great, thanks.

You can map sub's already to convert one message type to another, so maybe, if instead of returning an Option[Msg] we just return a List[Msg], we might be able to modify things to allow one result to produce many messages.

I'll look into it as soon as I'm back in Tyrian mode.. :sweat_smile:

davesmith00000 commented 2 months ago

I've had another think about this, and also tried implementing it and my conclusion is... I need to think about it a bit more. :slightly_smiling_face:

The use case here is quite specific: When using a websocket (but it could be http too), you can receive several messages in a single payload/bundle from the server, and you like to chop them up, and send them out to be processed individually. Fair enough. However I feel like this is a corner case.

The general case is that something happened, or it didn't, and if it did, please let the main app logic know so that it can be dealt with.

Now.... Option is a special case of List, so maybe I'm splitting hairs, but right now it feels like Option better represents the general case, and dissuades people from trying to be too clever in their subs.

The workaround, is to put the many-part server messages into a single Msg, and let the update function decide what to do next, which could either be to just get on with the processing of the batch immediately, or split into more Msgs and send them round but emitting them from Cmds. Which of those is the correct approach might be use-case dependant, but I'm incline to prefer updating the batch immediately, because what benefit is there of going round the loop again individually (even if Sub could produce many messages - what is the benefit?).

Sometimes I myself make issues that express some frustration with the APIs while I'm trying to do something, and then close them once I've stopped to think about the implications of the change and what it all means. I'm not saying this is definitely one of those times, but it feels a bit like it might be.

More thought needed.