<div dir="auto">Another flavor would be to leave liftF alone and add a method that does the same thing with a different name. This would preserve performance characteristics for instances like FT, for situations where the current implementation is faster.</div><br><div class="gmail_quote"><div dir="ltr" class="gmail_attr">On Fri, Jul 16, 2021, 4:55 PM David Feuer <<a href="mailto:david.feuer@gmail.com">david.feuer@gmail.com</a>> wrote:<br></div><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex"><div dir="auto">We have<div dir="auto"><br></div><div dir="auto">class Monad m => MonadFree f m | m -> f where</div><div dir="auto">  wrap :: f (m a) -> m a</div><div dir="auto"><br></div><div dir="auto">liftF :: (Functor f, MonadFree f m) => f a -> m a</div><div dir="auto">liftF = wrap . fmap pure</div><div dir="auto"><br></div><div dir="auto">I propose we change this to</div><div dir="auto"><br></div><div dir="auto">class Monad m => MonadFree f m | m -> f where</div><div dir="auto">  wrap :: f (m a) -> m a</div><div dir="auto"><br></div><div dir="auto">  liftF :: f a -> m a</div><div dir="auto"><div dir="auto" style="font-family:sans-serif">  default liftF :: Functor f => f a -> m a</div><div dir="auto" style="font-family:sans-serif">  liftF = wrap . fmap pure</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">and add a function</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">defaultWrap :: MonadFree f m => f (m a) -> m a</div><div dir="auto" style="font-family:sans-serif">defaultWrap = join . liftF</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">This change is not strictly backwards compatible. Some instances might, hypothetically, have to add a Functor constraint. For example, the classic Control.Monad.Free and Control.Monad.Trans.Free would need them. However, those instances already have (currently redundant) Functor constraints, so that doesn't seem like a big deal.</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto" style="font-family:sans-serif">An alternative would be to hew more strictly to backwards compatibility by placing a Functor f constraint on liftF. This seems a bit sad for "freer" instances that don't need it. For example, we have</div><div dir="auto" style="font-family:sans-serif"><br></div><div dir="auto"><div dir="auto"><font face="sans-serif">newtype FT f m a =</font> <span style="font-family:sans-serif">FT</span></div><div dir="auto"><span style="font-family:sans-serif">  {</span> <span style="font-family:sans-serif">runFT :: forall r. (a -> m r) -> (forall x. (x -> m r) -> f x -> m r) -> m r }</span></div><div style="font-family:sans-serif" dir="auto"><br></div><div style="font-family:sans-serif" dir="auto">for which</div><div style="font-family:sans-serif" dir="auto"><br></div><div style="font-family:sans-serif" dir="auto">liftF :: f a -> FT f m a</div><div style="font-family:sans-serif" dir="auto">liftF fa = FT $ \pur bndf -> bndf pur fa</div><div style="font-family:sans-serif" dir="auto"><br></div><div style="font-family:sans-serif" dir="auto">Pull request at <a href="https://github.com/ekmett/free/pull/208" target="_blank" rel="noreferrer">https://github.com/ekmett/free/pull/208</a></div></div></div></div>
</blockquote></div>