Re: Proposal: make liftF a method of MonadFree
Edward Kmett <[email protected]> Sat, 17 Jul 2021 07:20:15 -0700
| Newsgroups | gmane.comp.lang.haskell.libraries |
|---|---|
| Message-ID | <CAJumaK9EaERQGFaz5MzkeoCQ77m88YHJ3g3PwteoPkAGRQ9Juw@mail.gmail.com> |
On Sat, Jul 17, 2021 at 5:32 AM Carter Schonwald <[email protected]> wrote: > What about having the class head be > > Monad m, Functor f=> … MonadFree f m .. > ? > Requiring f to be a Functor would be too strong. Plenty of these are not. > Is the motivation here to have a more performant liftF? > > What are some examples of more efficient implementations for current > instances and what’s the performance delta? > Often the difference can be a walk of the entire structure. I'm not allergic to the idea of just adding the method to the class as proposed, it already has a superclass, so it is already a record internally, etc. > On Fri, Jul 16, 2021 at 6:53 PM David Feuer <[email protected]> wrote: > >> 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. >> >> On Fri, Jul 16, 2021, 4:55 PM David Feuer <[email protected]> wrote: >> >>> We have >>> >>> class Monad m => MonadFree f m | m -> f where >>> wrap :: f (m a) -> m a >>> >>> liftF :: (Functor f, MonadFree f m) => f a -> m a >>> liftF = wrap . fmap pure >>> >>> I propose we change this to >>> >>> class Monad m => MonadFree f m | m -> f where >>> wrap :: f (m a) -> m a >>> >>> liftF :: f a -> m a >>> default liftF :: Functor f => f a -> m a >>> liftF = wrap . fmap pure >>> >>> and add a function >>> >>> defaultWrap :: MonadFree f m => f (m a) -> m a >>> defaultWrap = join . liftF >>> >>> 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. >>> >>> 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 >>> >>> newtype FT f m a = FT >>> { runFT :: forall r. (a -> m r) -> (forall x. (x -> m r) -> f x -> m >>> r) -> m r } >>> >>> for which >>> >>> liftF :: f a -> FT f m a >>> liftF fa = FT $ \pur bndf -> bndf pur fa >>> >>> Pull request at https://github.com/ekmett/free/pull/208 >>> >> _______________________________________________ >> Libraries mailing list >> [email protected] >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > _______________________________________________ > Libraries mailing list > [email protected] > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > _______________________________________________ Libraries mailing list [email protected] http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries