Why do some of threepenny-gui FRP combinators operate on a MonadIO monad instead of being pure?
Asked Answered
E

2

7

First of all a disclaimer, I might have misunderstood completely the way threepenny-gui works due to my not so advanced knowledge of Haskell, so take my assertions with a grain of salt. :-)

It seems to me that some combinators are not pure e.g.

stepper :: MonadIO m => a -> Event a -> m (Behavior a)

Is stepper necessarily operating on some type of IO monad (and it's therefore not pure), or I'm I misunderstanding something here ? Second question, if those combinators are indeed unpure, why is that ? To me this makes the code for building the event graph much less nice then reactive-banana since one has to use an IO monad instead of being just pure plain functions. The code seems to just become more complicated then in reactive-banana.

Even more scary, valueChange appears to be pure from the type

valueChange :: Element -> Event String

but it's actually using unsafeMapIO inside, so is it actually doing hidden IO ? Again, I might be misunderstanding something, but isn't this the most supreme sin in Haskell ? It was also confusing, I couldn't tell from type how the call-back registration was happening... this never happened to me before in Haskell... Was this done to spare users having to deal with the Frameworks monad ?

Erelia answered 13/4, 2014 at 15:19 Comment(0)
P
5

I'm not a threepenny-gui user, but I can give you a bit of info on your questions from my reading of the code.

Firstly regarding stepper: It seems that internally threpenny-gui uses IORefs to keep track of accumulating parameters. That's why stepper requires MonadIO. However, I do not think this requires IO to build your event graph, it just means it has to run in IO. This shouldn't cause any awkwardness for you as an API user. If you think it does please post a more specific question.

Secondly regarding valueChange being implemented in terms of an unsafe function: Yes, this is very common to see in Haskell libraries. Haskell library authors often use unsafe functions when they know the way that they are used is actually safe for all possible execution paths, but it is not possible to prove this to the type system. In other words, library authors use unsafe operations in a safe way so that you don't have to!

Prokofiev answered 13/4, 2014 at 15:59 Comment(5)
In practice it means that if I want to do anything with the behaviour returned by stepper I have to first "unwrap" it from the IO monad, i.e. use >>= , fmap or do notation. That makes code much more complicated then in the case of reactive-banana where the event graph is not built using a Monad (The event network is built using the Frameworks monad, but that is for registering ins and outs, a different thing).Erelia
My understanding of the IO monad is that it exists to ensure referential transparency. As long as all io happens inside the IO monad referential transparency of all functions is maintained. Is referential transparency still maintained in practice when using functions such as valueChange ?Erelia
@miguel.negrao: Yes, Heinrich will have written that function very carefully, in a way that maintains referential transparency.Prokofiev
@miguel.negrao: In that case I guess the problem you are experiencing is not that the event graph must be written using MonadIO but that it must be written using a monad at all! I don't have any response to that other than to guess that that is the nature of the API.Prokofiev
@TomEllis Yes, indeed my point is that it needs a monad at all, which is not the case in reactive-banana.Erelia
S
2

This answer is complementary to Tom Ellis'. It covers how I would justify the monadic FRP combinators from the perspective of a Threepenny user.

With Threepenny, odds are that a large part of your event graph will be built from Elements, which live in UI (a thin wrapper for IO) because they are tied to a DOM in a browser. That being so, it makes sense to remove the indirection involved in externally plugging something like reactive-banana (as the old reactive-banana-threepenny package used to do). In the case of reactive-banana-threepenny the indirection was not limited to having to use the Frameworks monad, but it also involved converting between the different event types of reactive-banana and Threepenny (it is also worth considering that FRP in Threepenny is optional; you can also use events in a traditional, imperative fashion).

Overall, it is a trade-off: to eliminate some indirection and make the API more immediately accessible, some cleanliness in defining the event graphs is sacrificed. In my experience, however, the trade-off is worth it.

On a closing note, this discussion I had with Heinrich around the time of the switch away from reactive-banana-threepenny may shed some more light on the matter.

Stearin answered 13/4, 2014 at 18:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.