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

Is it ok to store sink inside the model? #750

Closed
functora opened this issue Oct 30, 2024 · 4 comments
Closed

Is it ok to store sink inside the model? #750

functora opened this issue Oct 30, 2024 · 4 comments

Comments

@functora
Copy link

I need to emit action on some async events like loading files from input component:

https://github.com/functora/functora.github.io/blob/4ca33c655deea34117e27b5feb2a03f13c432c96/ghcjs/miso-functora/src/Functora/Miso/Widgets/Field.hs#L344-L350

Because my input component is basically just a view, I need to make two things - create main application miso action, and emit async event which should land on update function of the main miso application. I'm using two corresponding arguments of my input component to do so:

https://github.com/functora/functora.github.io/blob/4ca33c655deea34117e27b5feb2a03f13c432c96/ghcjs/miso-functora/src/Functora/Miso/Widgets/Field.hs#L35-L36

Providing action argument is easy - basically it's just a mapping to main miso application action type. Providing emitter argument is a bit less straightforward, because my input component is basically a view, so all argument I'm providing I should get somehow from the model, because I don't have direct access to main miso app update function, effectSub and sink.

The workaround I'm using at the moment is to save MVar with the sink to the model on the initial main miso app update, so I can have access to it from my input component:

https://github.com/functora/functora.github.io/blob/4ca33c655deea34117e27b5feb2a03f13c432c96/ghcjs/delivery-calculator/src/Main.hs#L124-L125

Is it ok to do so, or there are better ways to emit async actions from components similar to my input component? Best regards!

@dmjio
Copy link
Owner

dmjio commented Oct 30, 2024

This isn't a bad idea per se. There really isn't a good way (that I see yet) to expose the global sink to users outside of the update function.

One alternative (so you can keep the model pure) is to do the {-# NOINLINE #-} unsafePerformIO trick to make the sink global.

globalSinkRef :: IORef (Sink Action)
{-# NOINLINE globalSinkRef #-}
globalSinkRef = unsafePerformIO $ newIORef (\_ -> pure ())

update InitGlobalSink m = 
  effectSub m $ \sink -> 
    m <# liftIO (writeIORef globalSinkRef sink)

Then you can just do readIORef globalSinkRef to get the sink from any IO action w/o polluting your model. Hope this helps.

Lastly, IORef should be fine over MVar, since the concurrency is handled internally, shouldn't need MVar necessarily.

TBH, we could add this to the library and users could make use of it exactly for circumstances like this.

@dmjio
Copy link
Owner

dmjio commented Oct 31, 2024

#751

@functora
Copy link
Author

Thanks @dmjio !

@dmjio
Copy link
Owner

dmjio commented Oct 31, 2024

Yea let me know how it works out for you

@dmjio dmjio closed this as completed Nov 1, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants