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