Open to including a "ControlTransformation" data type?
As MonadTrans relates to a natural transformation, MonadTransControl roughly relates to a type like this:
data ControlTransformation st m n = ControlTransformation
{ transWith :: !(forall a. ((forall x. n x -> m (st x)) -> m a) -> n a)
, restoreState :: !(forall a. st a -> n a)
}
type StatelessControlTransformation = ControlTransformation Identity
statelessControlTransformation
:: forall m n. (Functor m, Applicative n)
=> (forall a. ((forall x. n x -> m x) -> m a) -> n a)
-> StatelessControlTransformation m n
statelessTransWith
:: (Functor m)
=> StatelessControlTransformation m n
-> ((forall x. n x -> m x) -> m a) -> n a
Would this type belong somewhere in monad-control?
What is ControlTransformation useful for?
The application I want to use it for is a better solution to what I called "the instrumented capability problem" in the docs at https://hackage.haskell.org/package/eventuo11y-0.5.0.0/docs/Observe-Event-BackendModification.html.
Basically, if you're managing effects like:
data FooEffects m = FooEffects
{ countBars :: BarPath -> m Int
, checkpointFoo :: m ()
}
you'll often have a function like
hoistFoo :: (forall x. m x -> n x) -> FooEffects m -> FooEffects n
But this can't be written if one of your effects has m in negative position, i.e. the same cases where we want MonadBaseControl/MonadUnlift* in mtl style.
Instead of a natural transformation, though, you can hoist an effect record like this along a ControlTransformation
I see. Except we cannot convert StM to st, because StM takes a. There's an issue #39 for doing this, and I tried to implement it in #55, but it just isn't nice if Monad m (or Functor) doesn't imply forall x y. Coercible x y => Coercible (m x) (m y). (TL;DR dealing with Identity makes things quite inconvenient to use).
Right, I ran into that too. I think this might be solvable if we could take a parameter role as a constraint (Not sure how this would be spelt, a prettier/more general version of FirstParamIsRepresentational m) and then the coercion would be generated.
If this were fixed, would that impact your interest in adding this? Even though we can't straightforwardly do the conversion currently, it's still morally a concept pertaining to "monad control" in the same way the typeclasses here do.
Via @amesgen, one option might be to simply have forall x y. Coercible x y => Coercible (m x) (m y) as a constraint on your declaration... Not sure if that solves the problem, haven't looked into the details of your PR
c.f. https://oleg.fi/gists/posts/2019-07-31-fmap-coerce-coerce.html
Well I'm embarrassed that I recommended you your own post... :sweat_smile:
@phadej Yep, it works without fmapCoerce now https://github.com/basvandijk/monad-control/pull/64