Open paluh opened 4 years ago
Hi @paluh,
It's funny you bring up React integration, because I actually want to split that out into its own library so the core event and signal implementation can be used independently. This library started out just as an implementation for signals for reactive UI state modelling, because my main work project needed them.
I have looked at Bodil's library before, and didn't want to use it because of the impurity of the implementation in the FFI layer. It's modelled after part of the old Elm runtime, and would not work well with situations where you need the ability to dynamically manage your own subscriptions and transformations/compositions.
My original signal implementation was just a few helper functions using FRP.Event
from purescript-event combined with a Ref
for storing the most recent value. FRP.Event
has a nice and simple implementation, and I had used it before server-side, but pretty quickly we ran into issues with how it handled unsubscribing with React. Fixing this required almost completely replacing the internal subscription logic, making me less happy about continuing to build on top of FRP.Event
. And I wanted to experiment with FRP.
So for this library:
Wire.Event
models events in a very similar way to FRP.Event
with a few differences:
FRP.Event
after a new value has been emitted but before the subscriber has been notified, the subscriber will still be notified. If one of the subscribers is a React component, and the new value causes it to unmount a child component which is also a subscriber to the same event, the child is still notified after unmounting triggering a warning.FRP.Event
has different goals based on how it is intended to be used. I wanted to experiment with more complex, sequential, stateful data flows for applications and so gave both Wire.Event
and Wire.Signal
monad instances.The React-specific modules came after examining the implementation of Recoil.js and realising that so much of their complex and messy implementation could be replaced with monadic signals. I'm still not sure of its usefulness though.
Hi @robertdp,
It's funny you bring up React integration, because I actually want to split that out into its own library so the core event and signal implementation can be used independently. This library started out just as an implementation for signals for reactive UI state modelling, because my main work project needed them.
To be honest I have just quickly noticed the quick differences like react integration so I wasn't sure if there are deeper motives behind this implementation - sorry. It seems that it is good idea to separate react pieces if the core stands on its own. This will clear the picture a bit too I think.
I have looked at Bodil's library before (...)
The rest of your response is just wonderful project statement which should go directly into the README I think :-) Anybody then can really appreciate your design choices and understand objectives and become a happy user of this lib! When the info is missing some people like me can think: "I'm afraid of the ecosystem fragmentation - is there any difference in this implementation or should I just stick to the current standard like bodil lib?".
By the way - I'm still trying to understand the details of the monad instances. From a distance I think that I understand that it is "something like ContT" - am I right? ;-)
Not really like ContT
, but I'll try to give an example of Event
and Signal
that will hopefully be intuitive.
Event:
data InputMethod = Controller | MouseAndKeyboard
data InputEvent
= ControllerEvent Controller.Event
| MouseEvent Mouse.Event
| KeyboardEvent Keyboard.Event
switchInputMethod :: Event InputMethod
controllerEvents :: Event Controller.Event
mouseEvents :: Event Mouse.Event
keyboardEvents :: Event Keyboard.Event
inputMethod :: Event InputMethod
inputMethod = switchInputMethod <|> pure MouseAndKeyboard
getInputEvents :: InputMethod -> Event InputEvent
getInputEvents Controller = ControllerEvent <$> controllerEvents
getInputEvents MouseAndKeyboard = (MouseEvent <$> mouseEvents) <|> (KeyboardEvent <$> keyboardEvents)
inputEvents :: Event InputEvents
inputEvents = inputMethod >>= getInputEvents
Future events output by inputEvents
will depend on the values that come through inputMethod
. inputMethod
simple combines the values emitted by switchInputMethod
with an initial value of MouseAndKeyboard
. Note that these values are being treated as individual, discreet events.
An important point is that all state in this example is subscriber-dependent. If switchInputMethod
suddenly emits the value Controller
, then it will update inputMethod
and also inputEvents
for any current subscribers. If a new subscriber joins after this, for that subscriber it will be as if the input method switch never happened and they will receive mouse and keyboard events.
Signal:
data InputMethod = Controller | MouseAndKeyboard
data InputState
= ControllerState Controller.State
| MouseAndKeyboardState Mouse.State Keyboard.State
inputMethod :: Signal InputMethod
controllerState :: Signal Controller.State
mouseState :: Signal Mouse.State
keyboardState :: Signal Keyboard.State
getInputState :: InputMethod -> Signal InputState
getInputState Controller = ControllerState <$> controllerState
getInputState MouseAndKeyboard = MouseAndKeyboardState <$> mouseState <*> keyboardState
inputState :: Signal InputState
inputState = inputMethod >>= getInputState
Signals have a value at all times, so they are continuous as opposed to events which are discreet. Because of this a default input method doesn't need to be provided, and the meaning and semantics of the data flow also changes.
Values are also no longer subscriber-dependent, because signals are inherently stateful. Any issues with subscription timing from the event-based example do not apply here.
@paluh I finally got around to making a README.
Hi,
This library looks really interesting as it provides ready to use react-basic integration.
Would you be so kind and shortly describe what are the project objectives? Could you please tell me how the event implementation part differs from bodil/purescript-signal for example?