monad-control icon indicating copy to clipboard operation
monad-control copied to clipboard

Open to including a "ControlTransformation" data type?

Open shlevy opened this issue 3 years ago • 8 comments

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?

shlevy avatar Dec 16 '22 15:12 shlevy

What is ControlTransformation useful for?

phadej avatar Dec 16 '22 15:12 phadej

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

shlevy avatar Dec 16 '22 16:12 shlevy

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).

phadej avatar Dec 16 '22 16:12 phadej

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.

shlevy avatar Dec 16 '22 16:12 shlevy

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

shlevy avatar Dec 16 '22 16:12 shlevy

c.f. https://oleg.fi/gists/posts/2019-07-31-fmap-coerce-coerce.html

shlevy avatar Dec 16 '22 16:12 shlevy

Well I'm embarrassed that I recommended you your own post... :sweat_smile:

shlevy avatar Dec 16 '22 21:12 shlevy

@phadej Yep, it works without fmapCoerce now https://github.com/basvandijk/monad-control/pull/64

shlevy avatar Dec 17 '22 14:12 shlevy