I had a specific use-case that ensures a property while using a Consul session via the consul-haskell package; in order to make it type check a MonadBaseControl IO instance is needed, so I added one. Hopefully this is generally useful, so I don't need to maintain a forked version of propellor!
Patch is located in the MonadBaseControl
branch of my cloned git repo git clone git@github.com:hellertime/propellor.git
I'm not entirely opposed to it, but this does add another two dependencies that have to be installed on every host managed by propellor.
Also, I don't really understand the instance MonadBaseControl implementation. (And have always had that difficulty with monad-control, which is one of the reasons I've stopped using it.) This and not having anything to test it with makes me fear maintaining it.
It looks like it would be sufficient make Propellor derive MonadBase IO, and then the MonadBaseControl instance could be shipped in another package (or even implemented in your config.hs). Does that sound like a reasonable compromise?
Agree on all points. I would rather not add the dependencies to propellor proper either, but such was the requirement for this change. I'd be happy enough with the MonadBase IO derivation and implementing this externally, no argument here.
As for what it does I cribbed the implementation from the Snap server ( https://github.com/snapframework/snap/blob/ bda15d0a0f29b0107fd69fbb8b7e8cc5ce5fa7e4/src/Snap/Snaplet/Internal/Types.hs# L277), and it seems to work, essentially it is a way to take the outer transformer, and wrap it inside the inner Monad, but in such a way that the inner Monad now has access to the outer transformer !? Yeah, I'm still not fully grokking it myself, but it type checks and functions.
Anyway feel free to implement at your leisure, it does seem that I could even derive the MonadBase IO instance manually and not have to change Propellor in the least, though the auto-derived instance would seem like a simple and harmless addition.
Looking at the lifted-async that is what uses the MonadBaseControl instance in your use case, I have some concerns.
Its docs say "All the functions restore the monadic effects in the forked computation unless specified otherwise." I think that has bearing on the following situation:
Suppose that two Propellor monad actions are run concurrently by this:
Propellor's monad includes a Writer component, that accumulates [EndAction]. Since they are running concurrently, it seems likely that
foo
andbar
are using separate Writers. Propellor doesn't currently use a State monad, but suppose that was added to its stack. Thenfoo
andbar
would necessarily, I think, be manipulating independent copies of state.Now, what happens when
concurrently
finishes running them? We have two Writers and/or two States, that need to be merged somehow. I don't see anything in the library that lets it do an intelligent merge. (For example, it could notice that [EndAction] is a monoid and mappend the two values.)So, I think when it says it's a restoring the monadic effects, it means it's discarding any changes that might have been made to the Writer or State.
Is this a large problem for Propellor? Maybe not. EndActions rarely need to be added, and in fact only one property in all of Propellor currently adds an EndAction. But this could change; Propellor could get state in its monad. What then?
Now, I actually dealt with this problem in the Propellor.Property.Concurrent module. The code there threads the Writer v alues through the concurrent actions and merges them at the end. If MonadBaseControl provides a more principled way to do that, which lets lifted-async also be used safely, then that part of propellor could perhaps be changed to use it.
But, I don't know if this is a problem that MonadBaseControl deals with at all. It might be that its design is intended to be used for things like
bracket
, where there's no concurrency, and so not as much problem with getting different monadic states that need to be merged together. (Although inbracket foo bar baz
, if baz throws an exception part way through, there's an interesting question about what to do with any monadic state it may have accumulated.)