Comments (19)
I think the simplest solution is to not nest actions. Have a single application action type, state, and update function a la Redux. This is the architecture I use for larger applications.
from purescript-pux.
@chexxor The answer is not in SlamData. "Interpretation" is just a function as far as the library is concerned. When launching an application, requiring that the user provide m ~> Aff eff
or forall a. m a -> Aff eff a
means:
I, the library, need all effects to run in
Aff
, because that's how I do all my bookkeeping. I don't care that the user might be writing it in somem
. What I do care about is that they can tell me how to turn it into someAff
so I can run it.
The forall a
or ~>
or NaturalTransformation
is just a trick to enforce that the interpreter can't care about the return result; it can only care about how to interpret the effects.
If you look at that gist, you can see I've written some machinery that can launch Aff
components (in which case the interpreter is simply fixed as id
), or the user can provide an interpreter.
https://gist.github.com/natefaubion/9cde1d3f278fae28678ed714efb78496#file-startapp-purs-L127-L140
https://gist.github.com/natefaubion/9cde1d3f278fae28678ed714efb78496#file-startapp-purs-L150
And all I do is use that interpret
parameter to run the effects in Aff
https://gist.github.com/natefaubion/9cde1d3f278fae28678ed714efb78496#file-startapp-purs-L198
That's what I mean by "trivial". I am not doing any pattern matching in the library. By being polymorphic, it can't do anything with the provided effects, which is a good thing. But if the user provides some function that can translate it into something the library does know about (namely Aff
), then it can run it!
Does that help?
from purescript-pux.
We solve this in Halogen by remaining polymorphic over the base Monad for effects. By requiring all effects to be monomorphic over Aff
, users are forced to come up with ad hoc parameter passing schemes that could otherwise be solved by mtl effects like MonadAsk
, MonadState
, etc. Only at the root do we require that the user provide some interpretation of m ~> Aff eff
for their effect Monad. We use this at SlamData to provide app-wide configuration, global state, authentication, and API services in an abstract and testable way, with service dependencies expressed via type classes.
I think it would be great if Pux were written abstractly over effects, instead of requiring everyone to use Aff monomorphically. It's very easy to provide concrete type synonyms with Aff applied for simple use cases, but I'm convinced that large applications (like SlamData) absolutely need abstraction capabilities.
from purescript-pux.
from purescript-pux.
@natefaubion, is the code which implements that polymorphic thing feely available on slamdata's github repo? I have been wanting to add that to Pux, but I don't have enough understanding to implement it on my own.
from purescript-pux.
Here is what I believe @natefaubion is refering to:
https://gist.github.com/natefaubion/9cde1d3f278fae28678ed714efb78496#file-main-purs-L111-L138
@chexxor. I'd also be interested to see your code.
from purescript-pux.
@chexxor the machinery for such a change for a library is trivial. It's merely providing a function of m ~> Aff eff
when you launch your application. Then the main driver must call that interpreter when dispatching actions.
from purescript-pux.
The code linked to by @simg is an example implementation of some custom effect Monad, which lets one lift Aff actions or read from some global environment that exposes a read-only User
.
from purescript-pux.
@natefaubion, you say it's trivial, which is encouraging, but I can't make an image in my head how such an all-purpose interpreter works. Does this imply Free? Or do you literally mean just generic m and pattern-matching on the specific m which was used? I'll search slamdata code in a few hours to see if the answer is there.
from purescript-pux.
surely that isn't so much less modular as completely not modular?
I've just published what I've built so far with Pux: https://github.com/simg/pux-example just to help explain the challenges I'm coming across.
Is that completely the wrong approach or is Pux not intended for such large applications?
from purescript-pux.
Modularity is not sacrificed. You can always separate functions or types out as needed.
In larger applications, having the action types and updates in one place is far easier for me to manage than deeply nested actions and update functions. Sometimes it makes sense to nest things and string types together, but I would recommend doing that as needed.
from purescript-pux.
I don't suppose you're able to provide any examples?
from purescript-pux.
Not of a larger application. They're commercial projects so I can't easily share the code.
I will add a section and example to the guide about it later this week with the next release. Essentially, there's one big update function and a top-level action data type that has constructors for pretty much every action in the app. Essentially just put all your update logic in a root component.
from purescript-pux.
Thanks. I quite understand about not being able to release commercial code.
I'll await the updated guide with interest :)
from purescript-pux.
Here's how to do with Ref GlobalState
, like I described above. Before I started using Ref, I had to have children send their updates to the global state through the update
function. I showed that here, also. It was really awkward.
-- Layout.purs
init :: forall eff. Eff (dom :: DOM, ref :: REF | eff) State
init = do
-- I store Global.State in its own module. It has it's own Actions, too.
globalRef = newRef Global.initGlobal
pure
{ globalRef
-- Pass the globalRef to all children which want to use it.
, child1: Child1.init globalRef
, ...
}
applyGlobalAction :: forall e. Global.GlobalAction -> EffModel State Action (ref :: REF | e) -> EffModel State Action (ref :: REF | e)
applyGlobalAction globalAction effmodel =
{ state: effmodel.state, effects: (liftEff $ updateGlobalRef effmodel.state.globalRef globalAction) : effmodel.effects }
update :: Action -> State -> EffModel State Action ClientEffects
update (Child1 action) state =
-- All children which want to update the global state can
-- simply modify the Ref.
-- If you don't use a Ref, you have to
-- use pass GlobalAction back from the child's update function,
-- then apply it to the global state, which is stored at root.
applyGlobalAction (snd updatedChild)
$ mapEffects NavAction
$ mapState (state {
nav = _
})
$ fst updatedChild
where
updatedChild = Nav.update action state.nav
-- When the global state changes, or when loading the child view,
-- the root needs to send the new value to all children using
-- an Child1.OnLoad Action, or similar.
update (PageView route@(Item assetId)) state =
-- I stored the current route in global state, as well, so
-- I need to update that when the route changes.
applyGlobalAction (ChangeRoute route)
$ mapEffects ItemAction
$ mapState (
state
{ itemState = _
}
)
$ Item.update (Item.OnLoad route) state.itemState
-- Child1.purs
-- If not directly modifying the Ref, you'd have to modify the global state like this,
-- by passing a GlobalAction back in a Tuple with the EffModel.
-- It's relatively awkward.
update :: Action -> State -> Tuple (EffModel State Action ClientEffects) Global.GlobalAction
update (RequestLogout) state = Tuple (noEffects $ state) (Global.ChangeUser Nothing)
update (LoginAction action) state =
-- If the child has a child which uses global state, you'd need to thread the GlobalAction
-- through here like this while also storing its state.
-- It's also awkward.
lmap
( mapEffects Grandchild.Action <<<
(mapState (state { grandchildState = _ }))
)
(GrandChild1.update action state.grandchildState)
from purescript-pux.
thanks @chexxor, that's very helpful
from purescript-pux.
Any updates on the updated docs with examples of larger apps?
from purescript-pux.
One thing that seems like it would be a challenge with a single, global set of actions is maintaining the state of form elements? Particularly if you have a lot of data entry forms?
from purescript-pux.
I'm archiving most of the discussion threads. If you'd like to continue the discussion I'd prefer if you posted a question on StackOverflow or Gitter. Thanks.
from purescript-pux.
Related Issues (20)
- Combinator for adding incremented key attribute HOT 1
- defaultValue prop HOT 1
- Todomvc example doesn't work with React (instead of preact) HOT 1
- Single element children props is not an array with preact HOT 2
- Add react component as property to another react component (Material-UI) HOT 3
- onKey* events should use KeyboardEvent in handlers HOT 2
- Real world examples HOT 2
- Rendering large numbers of elements exceeds maximum call depth HOT 2
- Page refresh on a route results in 404 page not found HOT 1
- Pux.Router can't parse Facebook and Google login redirect query parameter HOT 1
- Permission to submit benchmark HOT 4
- Needs Aff 4.0 upgrade
- Event fires faster than state update
- Memoize doesn't appreciably improve performance HOT 3
- Is Pux still being mantained? HOT 1
- Plans to update for Purescript 0.12? HOT 5
- Release version 13 on pursuit HOT 5
- Starter app includes outdated bower, fails to build
- Starter app fails to build with error "Module Data.Comparison imports itself"
- Website is broken
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from purescript-pux.