-
Notifications
You must be signed in to change notification settings - Fork 149
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
Added a base monad capable of PerformEvent
, PostBuild
, and TriggerEvent
#76
Conversation
Most of the formatting is Hindent -> sanity checks. Let me know if I should reformat. |
Very cool example |
Looks like some pretty serious changes were just merged in. Having a hard time seeing how to change this PR to accommodate that. Also, it looks like there are a lot of classes that aren't instantiated on a lot of types. Is this a problem? |
To clarify the last part of that last comment: There are lots of |
Yes, that's a great point! Right now we have all the instances that reflex-dom needs, but it would be great to have the complete set. Are there any automated ways to figure out which ones might be missing? That would be pretty slick. |
Also, on updating the PR, is there any change in particular you're having trouble working through? I'd expect most of these changes to be compatible with what you're doing, at least at the idea level. |
I doubt that's something one could automate easily. It's the I've got a fix that compiles and will probably work. Just a little confused on the introduction of |
Yea, the I can't help but feel like the |
In some perf testing I did, ReaderT performed worse than a typeclass - I think it may present fewer opportunities for specialization. The idea with HasSpiderTimeline is that we would construct it using a Kmett-like "reflection" approach. I guess I didn't get around to actually doing that yet, though! What do you think of that concept? |
Hm weird. Never would have guessed that Anyway, I'm not familiar with how to do the reflection style approach. Do you know any examples I could look into? |
Yeah, I'm not sure about the perf for dynamically created SpiderTimelineEnvs - the common case for most applications is the Global case, so I optimized for that first. The "reflection approach" is basically to use this to dynamically generate an instance of HasSpiderTimeline. |
After exploring the newtype ReifiedSpiderTimeline x = ReifiedSpiderTimeline (SpiderTimelineEnv x)
instance Reifies s (ReifiedSpiderTimeline x) => HasSpiderTimeline x where
spiderTimeline = let ReifiedSpiderTimeline env = reflect (Proxy :: Proxy s) in env This is kind of a nonsense instance, and I don't think it can be implemented. I think And of course this instance wouldn't work instance Reifies s (ReifiedSpiderTimeline s) => HasSpiderTimeline s where
spiderTimeline = let ReifiedSpiderTimeline env = reflect (Proxy :: Proxy s) in env because making use of the instance would require pinning Ultimately, if the performance difference is small enough, I'd advocate for the |
Also worth noting that |
This compiles, but it's pretty gross. And data ReflectedSpiderTimeline x s
instance Reifies s (SpiderTimelineEnv x) =>
HasSpiderTimeline (ReflectedSpiderTimeline x s) where
spiderTimeline = reflected (Proxy :: Proxy s) $ reflect (Proxy :: Proxy s)
reflected
:: Proxy s
-> SpiderTimelineEnv x
-> SpiderTimelineEnv (ReflectedSpiderTimeline x s)
reflected _ = unsafeCoerce
-- | Pass a new timeline to the given function.
withSpiderTimeline :: (forall x. HasSpiderTimeline x => SpiderTimelineEnv x -> IO r) -> IO r
withSpiderTimeline k = do
env <- unsafeNewSpiderTimelineEnv
reify env $ \s -> k (reflected s env) This is relevant to this PR because it influences the design of |
Hmm, I'm confused about the specialization/inlining thing, but maybe it would be a good idea to run it by some of the GHC guys. Currently, in large apps, the specialization/inlining is already pretty bad for any medium to large apps, unless you turn on -fspecialize-to-spidertimeline-global, which massacres all this stuff anyhow. |
My understanding of the problem in this particular case is that GHC just completely fails to do any specializing when a rank-n type has constraints. rankn :: (forall m. MonadIO m => String -> m ()) -> IO ()
rankn f = f "hi"
unspecializable :: IO ()
unspecializable = rankn (liftIO . putStrLn) |
…e management stuff. Relevant to #76.
@ElvishJerricco I just added your solution, with a couple of things renamed slightly. |
Ah cool. Question: What's the proper git-ettiquette for situations like this, where upstream makes some merge-breaking commits before merging a PR? I'm tempted to rebase this branch on the new |
Forcing is fine for a branch like this, since you don't need to keep the history and nobody else is likely to have pulled that branch. |
This factors out a portion of `ImmediateDomBuilder` from `reflex-dom`
38d1c0b
to
62b68b4
Compare
Should EDIT: |
This includes the capabilites of `PostBuild`, `PerformEvent`, and `TriggerEvent`
@ElvishJerricco I've merged most of this, but not ReflexBase itself. |
What's the reason for leaving out |
It'd be good to get it in, but I wanted to get the other stuff in ASAP, since the other stuff is in parts of the code I'm changing rapidly. ReflexBase isn't likely to conflict with stuff even if it takes a couple of days to get in. In order to get it in, we need to do a few things:
One final thing: I think it would be better to move a bit more stuff into TriggerEvent. The use of |
Good points. I could have been more diligent on those three points. Moving more into |
I also think a more rigid convention should be followed with those classes and implementations. Specifically, each pair should have the modules |
This is where my name |
This morning I pushed a change that reorganized this stuff substantially. Together with the parts of this PR that I've already merged, I think "each pair should have the modules With regard to Reflex.Base, I see where you're coming from, and that makes sense. However, |
Ah, cool. I hadn't seen the other changes you made this morning. So then I guess the changes still needed then are the remainder of the For the |
That all sounds right to me. By the way, do you happen to have patches for reflex-dom in relation to these changes? |
None whatsoever. I'm guessing a couple of the monads in the |
@ElvishJerricco It looks like I can't reopen this, since I merged some of it, but I created #80 to track the remainder. |
This pull request adds a base monad
ReflexBase
capable of most of the functionality offered by the various Reflex typeclasses. In particular, it usesPerformEventT
andPostBuildT
to enable their corresponding typeclasses, and it uses a newTriggerEventT
monad transformer for theTriggerEvent
class. The motivation of this pull request is to enable users ofreflex
to write reactive programs in contexts other than web apps with the same relative ease. Having one monad with these three main classes on top ofSpiderHost
covers a large surface area of requirements.TriggerEventT
was factored out ofImmediateDomBuilder
fromreflex-dom
. As such, much of theWidget
monad can be replaced withReflexBase
. This includes most of the implementation ofattachWidget
, which was factored out intorunReflexBase
. I haven't changedreflex-dom
to make use of this refactoring, but it shouldn't be difficult.There is one breaking change. The
TriggerEvent
class now exists in its own module, instead of sitting in theReflex.PerformEvent.Class
module. This is for clarity, not a requirement.