Hi all, I propose we add a strict version of <$> to base: -- | A strict version of 'Data.Functor.<$>' for monads. (<$!>) :: Monad m => (a -> b) -> m a -> m b
f <$!> m = do a <- m return $! f a {-# INLINE (<$!>) #-} infixl 4 <$!> It works on Monads instead of Functors as required by us inspecting the argument.
This version is highly convenient if you want to work with functors/applicatives in e.g. parser and avoid spurious thunks at the same time. I realized that it was needed while fixing large space usage (but not space-leak) issues in cassava.
I believe Edward Kmett discovered the need for it independently as well. Deadline: 3 weeks Cheers, Johan _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
Obvious +1. On Fri, Nov 29, 2013 at 1:07 PM, Johan Tibell <[hidden email]> wrote:
Gregory Collins <[hidden email]> _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by Johan Tibell-2
I don't like that this function is implemented for Monads, I think that it makes
sense for some other functors as well. Though to do this 'properly' we would probably end up with another typeclass "StrictFunctor" or something, and that is perhaps too much unnecessary complexity. In the same vein as strict fmap, does a strict (<*>) make sense as well? -- | A strict version of `Control.Applicative.<*>` for monads (<*!>) :: Monad m => m (a -> b) -> m a -> m b mf <*!> mx = do f <- mf x <- mx return $! f x We might also call these fmap' and ap', but I prefer the operator. Twan On 29/11/13 12:07, Johan Tibell wrote: > Hi all, > > I propose we add a strict version of <$> to base: > > -- | A strict version of 'Data.Functor.<$>' for monads. > (<$!>) :: Monad m => (a -> b) -> m a -> m b > f <$!> m = do > a <- m > return $! f a > {-# INLINE (<$!>) #-} > > infixl 4 <$!> > > It works on Monads instead of Functors as required by us inspecting the argument. > > This version is highly convenient if you want to work with functors/applicatives > in e.g. parser and avoid spurious thunks at the same time. I realized that it > was needed while fixing large space usage (but not space-leak) issues in cassava. > > I believe Edward Kmett discovered the need for it independently as well. > > Deadline: 3 weeks > > Cheers, > Johan > > > > _______________________________________________ > Libraries mailing list > [hidden email] > http://www.haskell.org/mailman/listinfo/libraries > _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
On Fri, Nov 29, 2013 at 1:19 PM, Twan van Laarhoven <[hidden email]> wrote: I don't like that this function is implemented for Monads, I think that it makes sense for some other functors as well. Though to do this 'properly' we would probably end up with another typeclass "StrictFunctor" or something, and that is perhaps too much unnecessary complexity. Do you have an example of such a functor? -- Johan _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by Twan van Laarhoven
On 29.11.2013 13:19, Twan van Laarhoven wrote:
> In the same vein as strict fmap, does a strict (<*>) make sense as well? I think this brings up a good point: strictness annotations may make sense in multiple other scenarios, not just for fmap. Can't we encapsulate similar functionality in a separate function first, wait for it to settle, and then introduce infix versions of it if really necessary? What about seqM :: Monad m => m a -> m a seqM m = m >>= (return $!) This would allow local definitions of f <$!> x = seqM (f <$> x) mf <*!> mx = seqM (mf <*> mx) until the dust settles. If <$!> is really used in abundance, then add <$!> as an infix. The reason why I'm hesitant to introduce a new infix for this is because I think infix is generally less readable than letter-based names. I agree that infix is good to have for functions you use a lot -- to the point where the infix is the standard application, like >>= and <$> -- but for <$!> I don't see this (yet). David _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by Johan Tibell-2
On 29.11.2013 13:23, Johan Tibell wrote:
> On Fri, Nov 29, 2013 at 1:19 PM, Twan van Laarhoven > > I don't like that this function is implemented for Monads, I think > that it makes sense for some other functors as well. Though to do > this 'properly' we would probably end up with another typeclass > "StrictFunctor" or something, and that is perhaps too much > unnecessary complexity. > > > Do you have an example of such a functor? Async is a Functor (but not Applicative). When asyncs finish and you poll their result with `wait`, you potentially get back an IO <thunk>. (Just the first example that came to mind.) David _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by David Luposchainsky
On Fri, Nov 29, 2013 at 01:32:48PM +0100, David Luposchainsky wrote:
> On 29.11.2013 13:19, Twan van Laarhoven wrote: > > In the same vein as strict fmap, does a strict (<*>) make sense as well? > > I think this brings up a good point: strictness annotations may make > sense in multiple other scenarios, not just for fmap. Can't we > encapsulate similar functionality in a separate function first, wait for > it to settle, and then introduce infix versions of it if really necessary? > > What about > > seqM :: Monad m => m a -> m a > seqM m = m >>= (return $!) I think this is an excellent idea. It makes sense to address the issue in the simplest, most generic way possible first and then later provide specialised functions when they have been shown to have widespread real-world usage. Tom (PS I note this is yet another example of the invisible Thunk type constructor causing problems!) _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by David Luposchainsky
On 29 November 2013 23:35, David Luposchainsky
<[hidden email]> wrote: > On 29.11.2013 13:23, Johan Tibell wrote: >> On Fri, Nov 29, 2013 at 1:19 PM, Twan van Laarhoven >> >> I don't like that this function is implemented for Monads, I think >> that it makes sense for some other functors as well. Though to do >> this 'properly' we would probably end up with another typeclass >> "StrictFunctor" or something, and that is perhaps too much >> unnecessary complexity. >> >> >> Do you have an example of such a functor? > > Async is a Functor (but not Applicative). When asyncs finish and you > poll their result with `wait`, you potentially get back an IO <thunk>. > (Just the first example that came to mind.) Except wait :: Async a -> IO a, so it's actually in the IO monad (which _is_ a monad :p). I'm +1 -- Ivan Lazar Miljenovic [hidden email] http://IvanMiljenovic.wordpress.com _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
On 29.11.2013 13:42, Ivan Lazar Miljenovic wrote:
> On 29 November 2013 23:35, David Luposchainsky > <[hidden email]> wrote: > >> Async is a Functor (but not Applicative). When asyncs finish and you >> poll their result with `wait`, you potentially get back an IO <thunk>. >> (Just the first example that came to mind.) > > Except wait :: Async a -> IO a, so it's actually in the IO monad > (which _is_ a monad :p). Sure, but now you're in IO (and can $! in IO of course). But sometimes you may not want to `wait` just yet, and evaluate the Async's eventual result directly to WHNF, so that if you then `wait`, you get an already evaluated thing in IO. _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by David Luposchainsky
On Fri, Nov 29, 2013 at 1:32 PM, David Luposchainsky <[hidden email]> wrote:
I think this is a good idea. We still need to think about how to make it clear to users when they need to force things when writing functorial (sp?)/applicative/monadic code. It's quite easy to introduce additional thunks there as expressions are often pushed inside a lazy data type (e.g. the state monad pushes the value inside a lazy tuple). If you look at e.g. the applicative APIs or some code that use them (e.g. attoparsec), it's not obvious that you can shoot yourself in the foot pretty easily by introducing too many thunks and thus use more space than needed.
I ran into this in practice when working with attoparsec. If you use e.g. the provided sepBy combinator, you end up with a list of many thunks in it. We ended up adding strict versions of basically all the combinators to work around this.
-- Johan _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by Johan Tibell-2
On 29/11/13 12:23, Johan Tibell wrote:
> On Fri, Nov 29, 2013 at 1:19 PM, Twan van Laarhoven <[hidden email] > <mailto:[hidden email]>> wrote: > > I don't like that this function is implemented for Monads, I think that it > makes sense for some other functors as well. Though to do this 'properly' we > would probably end up with another typeclass "StrictFunctor" or something, > and that is perhaps too much unnecessary complexity. > > > Do you have an example of such a functor? > > -- Johan The first thing that came to mind was ZipList. Perhaps a more realistic example would be parsing combinator or FRP libraries that are applicative but not monadic. Twan _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
I'm trying to figure out whether this is a property of the functor itself. I guess it is, but at the same time it seems somewhat orthogonal whether to `seq` some value`. On Fri, Nov 29, 2013 at 3:50 PM, Twan van Laarhoven <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
could someone explain to me why this <$!> would be for monads rather being more generally also for functors or applicatives? On Fri, Nov 29, 2013 at 10:35 AM, Johan Tibell <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
On Fri, Nov 29, 2013 at 12:03:43PM -0500, Carter Schonwald wrote:
> could someone explain to me why this <$!> would be for monads rather being > more generally also for functors or applicatives? It's not clear whether such a thing can be implemented for a functor or applicative. It seemingly needs to exploit the fact that the next action in a bind can depend on the "value returned by" the previous action. Still, the semantics depend very much on the laziness properties of the monad in question. f <$!> m = do a <- m return $! f a data R x = R x data S x = S x data T x = T x instance Monad T where return = T m >>= f = T (case m of T m' -> case f m' of T y -> y) instance Monad S where return = S m >>= f = case m of S m' -> S (case f m' of S y -> y) -- Equivalent implementation -- S m' >>= f = S (case f m' of S y -> y) instance Monad R where return = R m >>= f = case m of R m' -> case f m' of R y -> R y -- Equivalent implementations: -- m >>= f = case m of R m' -> f m' -- R m' >>= f = f m' try :: Monad m => m Int -> () try l = (+1) <$!> l `seq` () *Main> try (undefined :: T Int) () *Main> try (T undefined :: T Int) () *Main> try (undefined :: S Int) *** Exception: Prelude.undefined *Main> try (S undefined :: S Int) () *Main> try (undefined :: R Int) *** Exception: Prelude.undefined *Main> try (R undefined :: R Int) *** Exception: Prelude.undefined Tom _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by Carter Schonwald
Figure out how to write one using either of those APIs, and I'll be quite impressed. =) I personally use this quite often when working in a monad, in lieu of `return $!` games. The argument for implementing a version of it in terms of Monad and not some new class off to the side of the class hierarchy that Monad doesn't subclass is that, frankly, if such a thing existed, I'd still have to use this combinator anyways when working with a transformer stack, etc. and would derive little benefit.
-Edward On Fri, Nov 29, 2013 at 12:03 PM, Carter Schonwald <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by Johan Tibell-2
>>>>> Johan Tibell <[hidden email]> writes:
> I propose we add a strict version of <$> to base: +1 John _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by Edward Kmett-2
I'm without a doubt overlooking something, but couldn't this be (otoh) fmap' :: Functor f => (a -> b) -> f a -> f b Nicolas On Nov 30, 2013 12:11 AM, "Edward Kmett" <[hidden email]> wrote:
_______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
No. Evaluation of your 'strictify' function will be delayed for the very
same reason that evaluation of 'f' is delayed in the first place. It doesn't matter what 'strictify' *does* if it's not even evaluated. * Nicolas Trangez <[hidden email]> [2013-11-30 02:11:02+0100] > I'm without a doubt overlooking something, but couldn't this be (otoh) > > fmap' :: Functor f => (a -> b) -> f a -> f b > fmap' f = fmap (strictify f) > where > strictify s = (($!) id) . s > > Nicolas > On Nov 30, 2013 12:11 AM, "Edward Kmett" <[hidden email]> wrote: > > > Figure out how to write one using either of those APIs, and I'll be quite > > impressed. =) > > > > I personally use this quite often when working in a monad, in lieu of > > `return $!` games. > > > > The argument for implementing a version of it in terms of Monad and not > > some new class off to the side of the class hierarchy that Monad doesn't > > subclass is that, frankly, if such a thing existed, I'd still have to use > > this combinator anyways when working with a transformer stack, etc. and > > would derive little benefit. > > > > -Edward > > > > > > On Fri, Nov 29, 2013 at 12:03 PM, Carter Schonwald < > > [hidden email]> wrote: > > > >> could someone explain to me why this <$!> would be for monads rather > >> being more generally also for functors or applicatives? > >> > >> > >> On Fri, Nov 29, 2013 at 10:35 AM, Johan Tibell <[hidden email]>wrote: > >> > >>> I'm trying to figure out whether this is a property of the functor > >>> itself. I guess it is, but at the same time it seems somewhat orthogonal > >>> whether to `seq` some value`. > >>> > >>> > >>> On Fri, Nov 29, 2013 at 3:50 PM, Twan van Laarhoven <[hidden email]>wrote: > >>> > >>>> On 29/11/13 12:23, Johan Tibell wrote: > >>>> > >>>>> On Fri, Nov 29, 2013 at 1:19 PM, Twan van Laarhoven <[hidden email] > >>>>> <mailto:[hidden email]>> wrote: > >>>>> > >>>>> I don't like that this function is implemented for Monads, I think > >>>>> that it > >>>>> makes sense for some other functors as well. Though to do this > >>>>> 'properly' we > >>>>> would probably end up with another typeclass "StrictFunctor" or > >>>>> something, > >>>>> and that is perhaps too much unnecessary complexity. > >>>>> > >>>>> > >>>>> Do you have an example of such a functor? > >>>>> > >>>>> -- Johan > >>>>> > >>>> > >>>> The first thing that came to mind was ZipList. Perhaps a more realistic > >>>> example would be parsing combinator or FRP libraries that are applicative > >>>> but not monadic. > >>>> > >>>> > >>>> Twan > >>>> > >>> > >>> > >>> _______________________________________________ > >>> Libraries mailing list > >>> [hidden email] > >>> http://www.haskell.org/mailman/listinfo/libraries > >>> > >>> > >> > >> _______________________________________________ > >> Libraries mailing list > >> [hidden email] > >> http://www.haskell.org/mailman/listinfo/libraries > >> > >> > > > > _______________________________________________ > > Libraries mailing list > > [hidden email] > > http://www.haskell.org/mailman/listinfo/libraries > > > > > _______________________________________________ > Libraries mailing list > [hidden email] > http://www.haskell.org/mailman/listinfo/libraries _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries signature.asc (853 bytes) Download Attachment |
On Nov 30, 2013 12:05 PM, "Roman Cheplyaka" <[hidden email]> wrote: Right, I see. Monad is required for the proper sequencing. Thanks! Nicolas > _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
In reply to this post by Roman Cheplyaka-2
On 2013-11-30 at 12:05:06 +0100, Roman Cheplyaka wrote:
> * Nicolas Trangez <[hidden email]> [2013-11-30 02:11:02+0100] >> I'm without a doubt overlooking something, but couldn't this be (otoh) >> >> fmap' :: Functor f => (a -> b) -> f a -> f b >> fmap' f = fmap (strictify f) >> where >> strictify s = (($!) id) . s > No. Evaluation of your 'strictify' function will be delayed for the very > same reason that evaluation of 'f' is delayed in the first place. > > It doesn't matter what 'strictify' *does* if it's not even evaluated. jfyi, if compiled with ghc -O2 -dsuppress-all -dsuppress-uniques -ddump-simpl it can be seen that fmap' is really almost the same as a plain fmap, except for an left-over eta-expansion which ghc doesn't optimize away: fmap' = \ @ a @ b @ f $dFunctor f1 -> fmap $dFunctor (\ x -> f1 x) _______________________________________________ Libraries mailing list [hidden email] http://www.haskell.org/mailman/listinfo/libraries |
Free forum by Nabble | Edit this page |