Skip to content

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

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

bearriver: Cannot apply runStateS__ to SF (StateT s m) a b values #174

Closed
Linearity opened this issue Jan 20, 2020 · 12 comments · May be fixed by #241
Closed

bearriver: Cannot apply runStateS__ to SF (StateT s m) a b values #174

Linearity opened this issue Jan 20, 2020 · 12 comments · May be fixed by #241
Labels

Comments

@Linearity
Copy link

BearRiver represents signal functions with a type that is polymorphic in the monad defining the type of the underlying monadic stream function, namely

type SF m = MSF (ReaderT DTime m)

This is nice because it offers users the opportunity to incorporate other monadic effects. But like many applications of monad transformers it forces one particular transformer, ReaderT DTime, to be at the top of whatever transformer stack you might want to use. That means that one must use lift to incorporate, say, state handling in signal functions.

If it instead used a MonadReader DTime m constraint in the style of the "mtl" package, it could be more flexible. There is a demand for this flexibility. Is this a reasonable thing to do in BearRiver?

The actual type synonym SF could not be defined this way, but the primitive signal functions could be. For instance, the type of edge could be:

edge :: MonadReader DTime m => MSF m Bool (Event ())

Then it would be easy to use edge in a signal function whose monad has StateT at the top. This would be nice, because then we could apply runStateS__ before passing the result to reactimate, which naturally applies runReaderS. As it stands, I believe, we must apply runStateS__ after reactimate, meaning that the entirety of the composite signal function could use only one state type for everything. That is a tremendous limitation, and it seems relatively straightforward to remove.

@ivanperez-keera
Copy link
Owner

So, from what I understand:

  • Bearriver could still define type SF m = MSF (ReaderT DTime m)
  • All functions in bearriver would need to use MonadReader DTime m => MSF m in place of SF m, and this would change all the documentation and the error message, but would be functionally the same.
  • FRP.Yampa.SF would still be Bearriver.SF Identity.

This would require a fair amount of work, especially after 54e81ab.

Is that correct?

@turion Opinions?

@Linearity I probably would not want to immediately put in the work, but I'm open to accepting a patch (I'd suggest that we all agree here first, otherwise the work might be for nothing).

@ivanperez-keera
Copy link
Owner

Of course, the more tests and benchmarks we have, the better.

@Linearity
Copy link
Author

Bearriver could still define type SF m = MSF (ReaderT DTime m)

Yes.

All functions in bearriver would need to use MonadReader DTime m => MSF m in place of SF m, and this would change all the documentation and the error message, but would be functionally the same.

Yes. I have been able to replace references to, say, BearRiver.edge with a local definition that is identical to the library definition sans type signature. If it were more complicated I would not have proposed this change right now.

FRP.Yampa.SF would still be Bearriver.SF Identity.

Yes.

@ivanperez-keera
Copy link
Owner

What would be the signatures of:

reactimate :: Monad m => m a -> (Bool -> m (DTime, Maybe a)) -> (Bool -> b -> m Bool) -> SF Identity a b -> m ()
evalAtZero :: SF Identity a b -> a -> (b, SF Identity a b)
evalAt     :: SF Identity a b -> DTime -> a -> (b, SF Identity a b)

@ivanperez-keera
Copy link
Owner

Everything else (at least) compiles with minimal changes.

The monad in reactimate is not the same for the sensing actions and the SF.

For FRP.Yampa, those three functions could be specialized for ReaderT DTime Identity, using runIdentity . <fs> for all three functions.

@turion
Copy link
Contributor

turion commented Jan 21, 2020

That means that one must use lift to incorporate, say, state handling in signal functions.

That I don't understand/agree with. Even if StateT s is further down the transformer stack, the whole monad is still an instance of MonadState s, so you can simply use mtl or similar to incorporate state effects. (See https://hackage.haskell.org/package/mtl-2.2.2/docs/Control-Monad-State-Class.html)

@Linearity Or what exactly do you mean by "state handling"?

@turion
Copy link
Contributor

turion commented Jan 21, 2020

@Linearity apologies, I confused rhine and dunai/bearriver. I take it you're referring to functions like runStateS__, which cannot be applied to SF (StateT s m) a b.

In rhine, I've solved these issues by simply wrapping the same API for signal functions.
E.g. the random monad: http://hackage.haskell.org/package/rhine-0.5.1.1/docs/FRP-Rhine-ClSF-Random.html Handling the ReaderT transformer (this is similar to StateT): http://hackage.haskell.org/package/rhine-0.5.1.1/docs/FRP-Rhine-ClSF-Reader.html

This approach is simple because most monad transformers commute with ReaderT DTime.

@Linearity
Copy link
Author

Linearity commented Jan 21, 2020

@ivanperez-keera Yes, I imagined that reactimate, evalAt, and evalAtZero would each have the same type as before. I wanted arbitrary transformer stacks, but all with ReaderT DTime at the bottom so that I can run the other effects before passing the result to reactimate.

@turion I think you're right, I was mistaken. If I were happy with ReaderT DTime being at the top, I could use get :: MonadState s m => m s from "mtl" in a signal function and compose it with a primitive signal function from BearRiver, even though the latter uses ask :: Monad m => ReaderT r m r from "transformers".

I take it you're referring to functions like runStateS__, which cannot be applied to SF (StateT s m) a b.

This is the real issue, yes. If I understand them correctly, the functions you referred to in Rhine can transform a monadic stream function of type

MSF (ReaderT (TimeInfo cl) (RandT g m)) a b

into one of type

MSF (ReaderT (TimeInfo cl) m) a b

Is that correct? Could BearRiver include such a facility as well? If so then I could run my StateT layer before reactimate runs the ReaderT layer, and the type could still be MSF (ReaderT DTime (StateT s m)) a b, a.k.a. SF (StateT s m) a b.

@turion
Copy link
Contributor

turion commented Jan 22, 2020

@Linearity Yes, that is about correct. BearRiver could include pretty much the same code like Rhine does. Simply replace ClSF cl by SF and you're good to go. This adds some code duplication between these two projects. Still, feel free to start a MR, I'll gladly help you with it.

This issue here is actually one of the reasons I opened #70 in order to avoid that duplication. Maybe it's time to revisit.

@Linearity Linearity changed the title BearRiver's SF does not use MonadReader bearriver: Cannot apply runStateS- functions to SF (StateT s m) a b values Jan 25, 2020
@Linearity Linearity changed the title bearriver: Cannot apply runStateS- functions to SF (StateT s m) a b values bearriver: Cannot apply runStateS__ to SF (StateT s m) a b values Jan 25, 2020
@ivanperez-keera
Copy link
Owner

I'm not really following the detour that this conversation took.

Also, I do not see why "the entirety of the composite signal function could use only one state type for everything" holds.

@ivanperez-keera
Copy link
Owner

Meaning, you can StateT s1 (StateT s2 m), no?

@turion
Copy link
Contributor

turion commented Jan 26, 2020

I'm not really following the detour that this conversation took.

My takeaway from this issue is that we need the analogous functionality like the Control.Monad.Trans.MSF.* modules, but specialised to bearriver SFs. This can be easily implemented in the same way as e.g. http://hackage.haskell.org/package/rhine-0.5.1.1/docs/FRP-Rhine-ClSF-Random.html.

@ivanperez-keera ivanperez-keera changed the title bearriver: Cannot apply runStateS__ to SF (StateT s m) a b values bearriver: Cannot apply runStateS__ to SF (StateT s m) a b values May 7, 2022
Repository owner locked and limited conversation to collaborators Apr 24, 2023
@ivanperez-keera ivanperez-keera converted this issue into discussion #361 Apr 24, 2023

This issue was moved to a discussion.

You can continue the conversation there. Go to discussion →

Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

3 participants