From fumiexcel at gmail.com Fri May 3 02:56:05 2019 From: fumiexcel at gmail.com (Fumiaki Kinoshita) Date: Fri, 3 May 2019 11:56:05 +0900 Subject: Proposal: give a field name to Data.Ord.Down Message-ID: It's awkward to write a lambda to unwrap this kind of wrappers. I propose newtype Down a = Down { getDown :: a } with the Show/Read instances as if they didn't have named fields. The same goes for Control.Arrow.ArrowMonad, although I'm not sure about its usefulness... -------------- next part -------------- An HTML attachment was scrubbed... URL: From davean at xkcd.com Fri May 3 03:13:03 2019 From: davean at xkcd.com (davean) Date: Thu, 2 May 2019 23:13:03 -0400 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: I have needed this many times. On Thu, May 2, 2019 at 10:56 PM Fumiaki Kinoshita wrote: > It's awkward to write a lambda to unwrap this kind of wrappers. I propose > > newtype Down a = Down { getDown :: a } > > with the Show/Read instances as if they didn't have named fields. > > The same goes for Control.Arrow.ArrowMonad, although I'm not sure about > its usefulness... > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From tanuki at gmail.com Fri May 3 18:39:10 2019 From: tanuki at gmail.com (Theodore Lief Gannon) Date: Fri, 3 May 2019 11:39:10 -0700 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: I just added `Down` to the RIO prelude, and the absence of a deconstructor was my #1 concern about doing so. It invites another package to provide an alternative, and we avoid including things with actively competing implementations. I'd much rather see this handled in `base`. (I'd rather have it named `unDown`, so as not to steal the name `getDown` from the heroes who will pair it with `getFunky`. But that's a lesser concern.) On Thu, May 2, 2019 at 8:13 PM davean wrote: > I have needed this many times. > > On Thu, May 2, 2019 at 10:56 PM Fumiaki Kinoshita > wrote: > >> It's awkward to write a lambda to unwrap this kind of wrappers. I propose >> >> newtype Down a = Down { getDown :: a } >> >> with the Show/Read instances as if they didn't have named fields. >> >> The same goes for Control.Arrow.ArrowMonad, although I'm not sure about >> its usefulness... >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From ekmett at gmail.com Sat May 4 07:28:58 2019 From: ekmett at gmail.com (Edward Kmett) Date: Sat, 4 May 2019 17:28:58 +1000 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: +1 from me, with no preference between getDown and runDown -Edward > On May 3, 2019, at 1:13 PM, davean wrote: > > I have needed this many times. > >> On Thu, May 2, 2019 at 10:56 PM Fumiaki Kinoshita wrote: >> It's awkward to write a lambda to unwrap this kind of wrappers. I propose >> >> newtype Down a = Down { getDown :: a } >> >> with the Show/Read instances as if they didn't have named fields. >> >> The same goes for Control.Arrow.ArrowMonad, although I'm not sure about its usefulness... >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries -------------- next part -------------- An HTML attachment was scrubbed... URL: From zemyla at gmail.com Sat May 4 08:16:59 2019 From: zemyla at gmail.com (Zemyla) Date: Sat, 4 May 2019 03:16:59 -0500 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: +1 from me, but the field name should be "up". On Sat, May 4, 2019, 02:29 Edward Kmett wrote: > +1 from me, with no preference between getDown and runDown > > -Edward > > On May 3, 2019, at 1:13 PM, davean wrote: > > I have needed this many times. > > On Thu, May 2, 2019 at 10:56 PM Fumiaki Kinoshita > wrote: > >> It's awkward to write a lambda to unwrap this kind of wrappers. I propose >> >> newtype Down a = Down { getDown :: a } >> >> with the Show/Read instances as if they didn't have named fields. >> >> The same goes for Control.Arrow.ArrowMonad, although I'm not sure about >> its usefulness... >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From eacameron at gmail.com Sat May 4 14:08:29 2019 From: eacameron at gmail.com (Elliot Cameron) Date: Sat, 4 May 2019 10:08:29 -0400 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: I can getDown with this. +1 On Sat, May 4, 2019, 4:17 AM Zemyla wrote: > +1 from me, but the field name should be "up". > > On Sat, May 4, 2019, 02:29 Edward Kmett wrote: > >> +1 from me, with no preference between getDown and runDown >> >> -Edward >> >> On May 3, 2019, at 1:13 PM, davean wrote: >> >> I have needed this many times. >> >> On Thu, May 2, 2019 at 10:56 PM Fumiaki Kinoshita >> wrote: >> >>> It's awkward to write a lambda to unwrap this kind of wrappers. I propose >>> >>> newtype Down a = Down { getDown :: a } >>> >>> with the Show/Read instances as if they didn't have named fields. >>> >>> The same goes for Control.Arrow.ArrowMonad, although I'm not sure about >>> its usefulness... >>> _______________________________________________ >>> Libraries mailing list >>> Libraries at haskell.org >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From carter.schonwald at gmail.com Sat May 4 18:11:24 2019 From: carter.schonwald at gmail.com (Carter Schonwald) Date: Sat, 4 May 2019 14:11:24 -0400 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: +1 On Sat, May 4, 2019 at 10:08 AM Elliot Cameron wrote: > I can getDown with this. +1 > > On Sat, May 4, 2019, 4:17 AM Zemyla wrote: > >> +1 from me, but the field name should be "up". >> >> On Sat, May 4, 2019, 02:29 Edward Kmett wrote: >> >>> +1 from me, with no preference between getDown and runDown >>> >>> -Edward >>> >>> On May 3, 2019, at 1:13 PM, davean wrote: >>> >>> I have needed this many times. >>> >>> On Thu, May 2, 2019 at 10:56 PM Fumiaki Kinoshita >>> wrote: >>> >>>> It's awkward to write a lambda to unwrap this kind of wrappers. I >>>> propose >>>> >>>> newtype Down a = Down { getDown :: a } >>>> >>>> with the Show/Read instances as if they didn't have named fields. >>>> >>>> The same goes for Control.Arrow.ArrowMonad, although I'm not sure about >>>> its usefulness... >>>> _______________________________________________ >>>> Libraries mailing list >>>> Libraries at haskell.org >>>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>>> >>> _______________________________________________ >>> Libraries mailing list >>> Libraries at haskell.org >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> >>> _______________________________________________ >>> Libraries mailing list >>> Libraries at haskell.org >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From chessai1996 at gmail.com Sat May 4 20:20:30 2019 From: chessai1996 at gmail.com (chessai .) Date: Sat, 4 May 2019 16:20:30 -0400 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: +1 On Thu, May 2, 2019, 10:56 PM Fumiaki Kinoshita wrote: > It's awkward to write a lambda to unwrap this kind of wrappers. I propose > > newtype Down a = Down { getDown :: a } > > with the Show/Read instances as if they didn't have named fields. > > The same goes for Control.Arrow.ArrowMonad, although I'm not sure about > its usefulness... > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From george at wils.online Sun May 5 01:49:09 2019 From: george at wils.online (George Wilson) Date: Sun, 5 May 2019 11:49:09 +1000 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: +1 On Sun., 5 May 2019, 06:21 chessai ., wrote: > +1 > > On Thu, May 2, 2019, 10:56 PM Fumiaki Kinoshita > wrote: > >> It's awkward to write a lambda to unwrap this kind of wrappers. I propose >> >> newtype Down a = Down { getDown :: a } >> >> with the Show/Read instances as if they didn't have named fields. >> >> The same goes for Control.Arrow.ArrowMonad, although I'm not sure about >> its usefulness... >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew.thaddeus at gmail.com Sun May 5 18:11:02 2019 From: andrew.thaddeus at gmail.com (Andrew Martin) Date: Sun, 5 May 2019 14:11:02 -0400 Subject: Proposal: give a field name to Data.Ord.Down In-Reply-To: References: Message-ID: +1. I do prefer getDown as the field name, but anything is better than nothing. Sent from my iPhone > On May 4, 2019, at 9:49 PM, George Wilson wrote: > > +1 > >> On Sun., 5 May 2019, 06:21 chessai ., wrote: >> +1 >> >>> On Thu, May 2, 2019, 10:56 PM Fumiaki Kinoshita wrote: >>> It's awkward to write a lambda to unwrap this kind of wrappers. I propose >>> >>> newtype Down a = Down { getDown :: a } >>> >>> with the Show/Read instances as if they didn't have named fields. >>> >>> The same goes for Control.Arrow.ArrowMonad, although I'm not sure about its usefulness... >>> _______________________________________________ >>> Libraries mailing list >>> Libraries at haskell.org >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries -------------- next part -------------- An HTML attachment was scrubbed... URL: From vanessa.mchale at iohk.io Wed May 8 01:56:50 2019 From: vanessa.mchale at iohk.io (Vanessa McHale) Date: Tue, 7 May 2019 20:56:50 -0500 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative Message-ID: It's relatively easy to define foldMapA, viz. foldMapA ::  (Monoid b, Traversable t, Applicative f) => (a -> f b) -> t a -> f b foldMapA = (fmap fold .) . traverse I've used found it useful once so far: http://hackage.haskell.org/package/dir-traverse-0.2.0.0/docs/src/System.Directory.Recursive.html#local-6989586621679024835 Cheers, Vanessa McHale -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From lanablack at amok.cc Wed May 8 02:04:17 2019 From: lanablack at amok.cc (Lana Black) Date: Wed, 08 May 2019 02:04:17 +0000 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: <9FEB27EF-F0AE-465C-A263-A5B105DCAA7C@amok.cc> On May 8, 2019 1:56:50 AM UTC, Vanessa McHale wrote: >It's relatively easy to define foldMapA, viz. > >foldMapA ::  (Monoid b, Traversable t, Applicative f) => (a -> f b) -> >t >a -> f b >foldMapA = (fmap fold .) . traverse > >I've used found it useful once so far: >http://hackage.haskell.org/package/dir-traverse-0.2.0.0/docs/src/System.Directory.Recursive.html#local-6989586621679024835 > >Cheers, >Vanessa McHale Yes, please! I have found it useful on numerous occasions. From david.feuer at gmail.com Wed May 8 02:49:10 2019 From: david.feuer at gmail.com (David Feuer) Date: Tue, 7 May 2019 22:49:10 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: On Tue, May 7, 2019, 9:57 PM Vanessa McHale wrote: > It's relatively easy to define foldMapA, viz. > > foldMapA :: (Monoid b, Traversable t, Applicative f) => (a -> f b) -> t > a -> f b > foldMapA = (fmap fold .) . traverse > That's a bit hard for me to read. Let's rewrite it a bit: foldMapA f = fmap fold . traverse f Looking at it more plainly, I can see that this traverses the container with f, producing a bunch of values, then maps under the functor to fold them. That smells funny. Let's fix it. fold :: (Foldable f, Monoid a) => f a -> a fold = foldMap id foldMapDefault :: (Traversable t, Monoid m) => (a -> m) -> t a -> m foldMapDefault f = getConst . traverse (Const . f) so foldMapA f = fmap (getConst . traverse Const) . traverse f By the functor composition law, we can write foldMapA f = fmap getConst . fmap (traverse Const) . traverse f By the traversable composition law, foldMapA f = fmap getConst . getCompose . traverse (Compose . fmap Const . f) This isn't looking so hot yet, but bear with me. fmap getConst doesn't actually do anything (it's operationally the same as fmap id = id), so we can ignore it). The functor we're traversing in is Compose f (Const b) (t x) where x can be anything. How does this functor behave? pure a = Compose (pure (pure a)) = Compose (pure (Const mempty)) liftA2 f (Compose x) (Compose y) = Compose (liftA2 (liftA2 f) x y) = Compose (liftA2 (\(Const p) (Const q) -> p <> q) x y) Whew! There are a lot of newtype wrappers, but let's ignore them. Does this Applicative instance look familiar? It should. It's operationally the same as the Monoid instance for Data.Monoid.Ap! So we can weaken Traversable to Foldable, and write foldMapA :: (Monoid b, Foldable t, Applicative f) => (a -> f b) -> t a -> f b foldMapA f = getAp . foldMap (Ap . f) But now it's so simple I'm not sure we need to define it anymore. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Wed May 8 02:59:00 2019 From: david.feuer at gmail.com (David Feuer) Date: Tue, 7 May 2019 22:59:00 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: TLDR: if you ever see anything that looks like fmap (foldMap f) . traverse g then you should generally rewrite it to getAp . foldMap (Ap . fmap f . g) In this case, f = id, so you just need getAp . foldMap (Ap . g) On Tue, May 7, 2019, 10:49 PM David Feuer wrote: > On Tue, May 7, 2019, 9:57 PM Vanessa McHale > wrote: > >> It's relatively easy to define foldMapA, viz. >> >> foldMapA :: (Monoid b, Traversable t, Applicative f) => (a -> f b) -> t >> a -> f b >> foldMapA = (fmap fold .) . traverse >> > > That's a bit hard for me to read. Let's rewrite it a bit: > > foldMapA f = fmap fold . traverse f > > Looking at it more plainly, I can see that this traverses the container > with f, producing a bunch of values, then maps under the functor to fold > them. That smells funny. Let's fix it. > > fold > :: (Foldable f, Monoid a) > => f a -> a > fold = foldMap id > > foldMapDefault > :: (Traversable t, Monoid m) > => (a -> m) -> t a -> m > foldMapDefault f = getConst . traverse (Const . f) > > so > > foldMapA f = fmap (getConst . traverse Const) . traverse f > > By the functor composition law, we can write > > foldMapA f = fmap getConst . fmap (traverse Const) . traverse f > > By the traversable composition law, > > foldMapA f = fmap getConst . getCompose . traverse (Compose . fmap Const > . f) > > This isn't looking so hot yet, but bear with me. fmap getConst doesn't > actually do anything (it's operationally the same as fmap id = id), so we > can ignore it). The functor we're traversing in is > > Compose f (Const b) (t x) > > where x can be anything. How does this functor behave? > > pure a > = Compose (pure (pure a)) > = Compose (pure (Const mempty)) > > liftA2 f (Compose x) (Compose y) > = Compose (liftA2 (liftA2 f) x y) > = Compose (liftA2 (\(Const p) (Const q) -> p <> q) x y) > > Whew! There are a lot of newtype wrappers, but let's ignore them. Does > this Applicative instance look familiar? It should. It's operationally the > same as the Monoid instance for Data.Monoid.Ap! So we can weaken > Traversable to Foldable, and write > > foldMapA > :: (Monoid b, Foldable t, Applicative f) > => (a -> f b) -> t a -> f b > foldMapA f = getAp . foldMap (Ap . f) > > But now it's so simple I'm not sure we need to define it anymore. > >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Wed May 8 03:37:12 2019 From: david.feuer at gmail.com (David Feuer) Date: Tue, 7 May 2019 23:37:12 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: My second to last comment was potentially non-optimal in unusual cases. If fmap is sufficiently expensive for the functor in question, and f is not id, then you might want to use import Data.Functor.Coyoneda lowerCoyoneda . getAp . foldMap (Ap . fmap f . liftCoyoneda . g) This is pretty similar to the composition of foldMap and traverse, but it doesn't have a Traversable constraint. On Tue, May 7, 2019, 10:59 PM David Feuer wrote: > TLDR: if you ever see anything that looks like > > fmap (foldMap f) . traverse g > > then you should generally rewrite it to > > getAp . foldMap (Ap . fmap f . g) > > In this case, f = id, so you just need > > getAp . foldMap (Ap . g) > > On Tue, May 7, 2019, 10:49 PM David Feuer wrote: > >> On Tue, May 7, 2019, 9:57 PM Vanessa McHale >> wrote: >> >>> It's relatively easy to define foldMapA, viz. >>> >>> foldMapA :: (Monoid b, Traversable t, Applicative f) => (a -> f b) -> t >>> a -> f b >>> foldMapA = (fmap fold .) . traverse >>> >> >> That's a bit hard for me to read. Let's rewrite it a bit: >> >> foldMapA f = fmap fold . traverse f >> >> Looking at it more plainly, I can see that this traverses the container >> with f, producing a bunch of values, then maps under the functor to fold >> them. That smells funny. Let's fix it. >> >> fold >> :: (Foldable f, Monoid a) >> => f a -> a >> fold = foldMap id >> >> foldMapDefault >> :: (Traversable t, Monoid m) >> => (a -> m) -> t a -> m >> foldMapDefault f = getConst . traverse (Const . f) >> >> so >> >> foldMapA f = fmap (getConst . traverse Const) . traverse f >> >> By the functor composition law, we can write >> >> foldMapA f = fmap getConst . fmap (traverse Const) . traverse f >> >> By the traversable composition law, >> >> foldMapA f = fmap getConst . getCompose . traverse (Compose . fmap >> Const . f) >> >> This isn't looking so hot yet, but bear with me. fmap getConst doesn't >> actually do anything (it's operationally the same as fmap id = id), so we >> can ignore it). The functor we're traversing in is >> >> Compose f (Const b) (t x) >> >> where x can be anything. How does this functor behave? >> >> pure a >> = Compose (pure (pure a)) >> = Compose (pure (Const mempty)) >> >> liftA2 f (Compose x) (Compose y) >> = Compose (liftA2 (liftA2 f) x y) >> = Compose (liftA2 (\(Const p) (Const q) -> p <> q) x y) >> >> Whew! There are a lot of newtype wrappers, but let's ignore them. Does >> this Applicative instance look familiar? It should. It's operationally the >> same as the Monoid instance for Data.Monoid.Ap! So we can weaken >> Traversable to Foldable, and write >> >> foldMapA >> :: (Monoid b, Foldable t, Applicative f) >> => (a -> f b) -> t a -> f b >> foldMapA f = getAp . foldMap (Ap . f) >> >> But now it's so simple I'm not sure we need to define it anymore. >> >>> -------------- next part -------------- An HTML attachment was scrubbed... URL: From isaace71295 at gmail.com Wed May 8 03:49:48 2019 From: isaace71295 at gmail.com (Isaac Elliott) Date: Wed, 8 May 2019 13:49:48 +1000 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: I've previously suggested similar things, like: allA :: (Applicative f, Foldable t) => (a -> f Bool) -> t a -> f Bool allA f = fmap getAll . getAp . foldMap (Ap . fmap All . f) I think such functions are very convenient. On Wed, 8 May 2019, 1:37 pm David Feuer, wrote: > My second to last comment was potentially non-optimal in unusual cases. If > fmap is sufficiently expensive for the functor in question, and f is not > id, then you might want to use > > import Data.Functor.Coyoneda > > lowerCoyoneda . getAp . foldMap (Ap . fmap f . liftCoyoneda . g) > > This is pretty similar to the composition of foldMap and traverse, but it > doesn't have a Traversable constraint. > > On Tue, May 7, 2019, 10:59 PM David Feuer wrote: > >> TLDR: if you ever see anything that looks like >> >> fmap (foldMap f) . traverse g >> >> then you should generally rewrite it to >> >> getAp . foldMap (Ap . fmap f . g) >> >> In this case, f = id, so you just need >> >> getAp . foldMap (Ap . g) >> >> On Tue, May 7, 2019, 10:49 PM David Feuer wrote: >> >>> On Tue, May 7, 2019, 9:57 PM Vanessa McHale >>> wrote: >>> >>>> It's relatively easy to define foldMapA, viz. >>>> >>>> foldMapA :: (Monoid b, Traversable t, Applicative f) => (a -> f b) -> t >>>> a -> f b >>>> foldMapA = (fmap fold .) . traverse >>>> >>> >>> That's a bit hard for me to read. Let's rewrite it a bit: >>> >>> foldMapA f = fmap fold . traverse f >>> >>> Looking at it more plainly, I can see that this traverses the container >>> with f, producing a bunch of values, then maps under the functor to fold >>> them. That smells funny. Let's fix it. >>> >>> fold >>> :: (Foldable f, Monoid a) >>> => f a -> a >>> fold = foldMap id >>> >>> foldMapDefault >>> :: (Traversable t, Monoid m) >>> => (a -> m) -> t a -> m >>> foldMapDefault f = getConst . traverse (Const . f) >>> >>> so >>> >>> foldMapA f = fmap (getConst . traverse Const) . traverse f >>> >>> By the functor composition law, we can write >>> >>> foldMapA f = fmap getConst . fmap (traverse Const) . traverse f >>> >>> By the traversable composition law, >>> >>> foldMapA f = fmap getConst . getCompose . traverse (Compose . fmap >>> Const . f) >>> >>> This isn't looking so hot yet, but bear with me. fmap getConst doesn't >>> actually do anything (it's operationally the same as fmap id = id), so we >>> can ignore it). The functor we're traversing in is >>> >>> Compose f (Const b) (t x) >>> >>> where x can be anything. How does this functor behave? >>> >>> pure a >>> = Compose (pure (pure a)) >>> = Compose (pure (Const mempty)) >>> >>> liftA2 f (Compose x) (Compose y) >>> = Compose (liftA2 (liftA2 f) x y) >>> = Compose (liftA2 (\(Const p) (Const q) -> p <> q) x y) >>> >>> Whew! There are a lot of newtype wrappers, but let's ignore them. Does >>> this Applicative instance look familiar? It should. It's operationally the >>> same as the Monoid instance for Data.Monoid.Ap! So we can weaken >>> Traversable to Foldable, and write >>> >>> foldMapA >>> :: (Monoid b, Foldable t, Applicative f) >>> => (a -> f b) -> t a -> f b >>> foldMapA f = getAp . foldMap (Ap . f) >>> >>> But now it's so simple I'm not sure we need to define it anymore. >>> >>>> _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Wed May 8 04:02:49 2019 From: david.feuer at gmail.com (David Feuer) Date: Wed, 8 May 2019 00:02:49 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: On Tue, May 7, 2019 at 11:49 PM Isaac Elliott wrote: > I've previously suggested similar things, like: > > allA :: (Applicative f, Foldable t) => (a -> f Bool) -> t a -> f Bool > allA f = fmap getAll . getAp . foldMap (Ap . fmap All . f) > > I think such functions are very convenient. > I think your allA is arguably better-justified than foldMapA because the simple implementation you demonstrate could run into trouble if fmap is expensive. Better: allA :: (Applicative f, Foldable t) => (a -> f Bool) -> t a -> f Bool allA f = getFall . foldMap (Fall . f) newtype Fall f = Fall {getFall :: f Bool} instance Applicative f => Semigroup (Fall f) where Fall x <> Fall y = Fall $ liftA2 (&&) x y instance Applicative f => Monoid (Fall f) where mempty = Fall (pure True) I keep wondering if there's some nice way (short of Coyoneda or similar) to generalize this sort of thing. I haven't thought of one yet. -------------- next part -------------- An HTML attachment was scrubbed... URL: From b at chreekat.net Wed May 8 04:12:28 2019 From: b at chreekat.net (Bryan Richter) Date: Wed, 8 May 2019 07:12:28 +0300 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: Hi David, At the risk of invoking the gods of Language Blorp, I will note that as a working programmer I know exactly what Applicative, Traversable, and Monoid are (from Vanessa's original proposal), but the unfortunately-named getAp is something I will only learn about begrudgingly. What you consider "so simple we don't need to define it" took a rather lengthy email to describe. Are you sure it's not worth actually defining? If nothing else, the next time someone searches Hoogle for a function matching its type signature, perhaps it will be an opportunity for someone like me to peer beneath the hood and learn something new. On Wed, 8 May 2019, 5.59 David Feuer, wrote: > TLDR: if you ever see anything that looks like > > fmap (foldMap f) . traverse g > > then you should generally rewrite it to > > getAp . foldMap (Ap . fmap f . g) > > In this case, f = id, so you just need > > getAp . foldMap (Ap . g) > > On Tue, May 7, 2019, 10:49 PM David Feuer wrote: > >> On Tue, May 7, 2019, 9:57 PM Vanessa McHale >> wrote: >> >>> It's relatively easy to define foldMapA, viz. >>> >>> foldMapA :: (Monoid b, Traversable t, Applicative f) => (a -> f b) -> t >>> a -> f b >>> foldMapA = (fmap fold .) . traverse >>> >> >> That's a bit hard for me to read. Let's rewrite it a bit: >> >> foldMapA f = fmap fold . traverse f >> >> Looking at it more plainly, I can see that this traverses the container >> with f, producing a bunch of values, then maps under the functor to fold >> them. That smells funny. Let's fix it. >> >> fold >> :: (Foldable f, Monoid a) >> => f a -> a >> fold = foldMap id >> >> foldMapDefault >> :: (Traversable t, Monoid m) >> => (a -> m) -> t a -> m >> foldMapDefault f = getConst . traverse (Const . f) >> >> so >> >> foldMapA f = fmap (getConst . traverse Const) . traverse f >> >> By the functor composition law, we can write >> >> foldMapA f = fmap getConst . fmap (traverse Const) . traverse f >> >> By the traversable composition law, >> >> foldMapA f = fmap getConst . getCompose . traverse (Compose . fmap >> Const . f) >> >> This isn't looking so hot yet, but bear with me. fmap getConst doesn't >> actually do anything (it's operationally the same as fmap id = id), so we >> can ignore it). The functor we're traversing in is >> >> Compose f (Const b) (t x) >> >> where x can be anything. How does this functor behave? >> >> pure a >> = Compose (pure (pure a)) >> = Compose (pure (Const mempty)) >> >> liftA2 f (Compose x) (Compose y) >> = Compose (liftA2 (liftA2 f) x y) >> = Compose (liftA2 (\(Const p) (Const q) -> p <> q) x y) >> >> Whew! There are a lot of newtype wrappers, but let's ignore them. Does >> this Applicative instance look familiar? It should. It's operationally the >> same as the Monoid instance for Data.Monoid.Ap! So we can weaken >> Traversable to Foldable, and write >> >> foldMapA >> :: (Monoid b, Foldable t, Applicative f) >> => (a -> f b) -> t a -> f b >> foldMapA f = getAp . foldMap (Ap . f) >> >> But now it's so simple I'm not sure we need to define it anymore. >> >>> _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Wed May 8 04:36:07 2019 From: david.feuer at gmail.com (David Feuer) Date: Wed, 8 May 2019 00:36:07 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: > Hi David, > > At the risk of invoking the gods of Language Blorp, I will note that as a > working programmer I know exactly what Applicative, Traversable, and Monoid > are (from Vanessa's original proposal), but the unfortunately-named getAp > is something I will only learn about begrudgingly. > That seems unfortunate. Learning to use such types is pretty useful. I'd recommend that every Haskell programmer get to know all the types in Data.Monoid and come to an understanding of what they're good for. > > What you consider "so simple we don't need to define it" took a rather > lengthy email to describe. Are you sure it's not worth actually defining? > So ... that long post was about trying to prove what I intuitively thought *must* be true. In the end, I wasn't quite able to finish the proof, but I did at least manage to convince myself that my intuition was correct. It's true that this sort of intuition takes a certain amount of time to develop. In the case of a really important operation, yeah, we should package it up. But is this operation important enough? I'm not really convinced yet. If nothing else, the next time someone searches Hoogle for a function > matching its type signature, perhaps it will be an opportunity for someone > like me to peer beneath the hood and learn something new. > That's valid. But ... there are lots of opportunities for that sort of thing already. Is it worth the API clutter to add another one? -------------- next part -------------- An HTML attachment was scrubbed... URL: From kovanikov at gmail.com Thu May 9 03:50:27 2019 From: kovanikov at gmail.com (Dmitriy Kovanikov) Date: Thu, 9 May 2019 11:50:27 +0800 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: I would like to add one more point of reference to the discussion. The `foldMapA` function is also implemented in the `relude` alternative prelude: http://hackage.haskell.org/package/relude-0.5.0/docs/src/Relude.Foldable.Fold.html#foldMapA And the implementation already uses `Ap` and `getAp` as was discussed here. Previous implementation used `fmap` and `traverse` but it was changed to a more efficient one. One possible improvement: instead of current implementation > foldMapA f = getAp . foldMap (Ap . f) It can be slightly more efficient (I guess) by using #. operator to coerce newtypes > foldMapA f = getAp #. foldMap (Ap . f) The implementation in `relude` also contains recommended order of variables under `forall`. After using `foldMapA` in production for a while we've figured out in what order variables should go to resolve most often ambiguities via TypeApplication. On Wed, May 8, 2019 at 12:36 PM David Feuer wrote: > On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: > >> Hi David, >> >> At the risk of invoking the gods of Language Blorp, I will note that as a >> working programmer I know exactly what Applicative, Traversable, and Monoid >> are (from Vanessa's original proposal), but the unfortunately-named getAp >> is something I will only learn about begrudgingly. >> > > That seems unfortunate. Learning to use such types is pretty useful. I'd > recommend that every Haskell programmer get to know all the types in > Data.Monoid and come to an understanding of what they're good for. > >> > >> What you consider "so simple we don't need to define it" took a rather >> lengthy email to describe. Are you sure it's not worth actually defining? >> > > So ... that long post was about trying to prove what I intuitively thought > *must* be true. In the end, I wasn't quite able to finish the proof, but I > did at least manage to convince myself that my intuition was correct. It's > true that this sort of intuition takes a certain amount of time to develop. > In the case of a really important operation, yeah, we should package it up. > But is this operation important enough? I'm not really convinced yet. > > > If nothing else, the next time someone searches Hoogle for a function >> matching its type signature, perhaps it will be an opportunity for someone >> like me to peer beneath the hood and learn something new. >> > > That's valid. But ... there are lots of opportunities for that sort of > thing already. Is it worth the API clutter to add another one? > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From vanessa.mchale at iohk.io Thu May 9 14:28:14 2019 From: vanessa.mchale at iohk.io (Vanessa McHale) Date: Thu, 9 May 2019 09:28:14 -0500 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: I appreciate the more efficient version, but I do not consider a package like relude to be an alternative to base (due to dependencies, maintenance). On 5/8/19 10:50 PM, Dmitriy Kovanikov wrote: > I would like to add one more point of reference to the discussion. The > `foldMapA` function is also implemented in the `relude` alternative > prelude: > > http://hackage.haskell.org/package/relude-0.5.0/docs/src/Relude.Foldable.Fold.html#foldMapA > > And the implementation already uses `Ap` and `getAp` as was discussed > here. Previous implementation used `fmap` and `traverse` but it was > changed to a more efficient one. > > One possible improvement: instead of current implementation > > > foldMapA f = getAp . foldMap (Ap . f) > > It can be slightly more efficient (I guess) by using #. operator to > coerce newtypes > > > foldMapA f = getAp #. foldMap (Ap . f) > > The implementation in `relude` also contains recommended order of > variables under `forall`. After using `foldMapA` in production for a > while we've figured out in what order variables should go to resolve > most often ambiguities via TypeApplication. > > On Wed, May 8, 2019 at 12:36 PM David Feuer > wrote: > > On Wed, May 8, 2019, 12:12 AM Bryan Richter > wrote: > > Hi David, > > At the risk of invoking the gods of Language Blorp, I will > note that as a working programmer I know exactly what > Applicative, Traversable, and Monoid are (from Vanessa's > original proposal), but the unfortunately-named getAp is > something I will only learn about begrudgingly. > > > That seems unfortunate. Learning to use such types is pretty > useful. I'd recommend that every Haskell programmer get to know > all the types in Data.Monoid and come to an understanding of what > they're good for. > > > What you consider "so simple we don't need to define it" took > a rather lengthy email to describe. Are you sure it's not > worth actually defining? > > > So ... that long post was about trying to prove what I intuitively > thought *must* be true. In the end, I wasn't quite able to finish > the proof, but I did at least manage to convince myself that my > intuition was correct. It's true that this sort of intuition takes > a certain amount of time to develop. In the case of a really > important operation, yeah, we should package it up. But is this > operation important enough? I'm not really convinced yet. > > > If nothing else, the next time someone searches Hoogle for a > function matching its type signature, perhaps it will be an > opportunity for someone like me to peer beneath the hood and > learn something new. > > > That's valid. But ... there are lots of opportunities for that > sort of thing already. Is it worth the API clutter to add another one? > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From carter.schonwald at gmail.com Thu May 9 14:35:16 2019 From: carter.schonwald at gmail.com (Carter Schonwald) Date: Thu, 9 May 2019 10:35:16 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: Its a complicated landscape, and we're still learning. if a new combinator is hard to write: a) how do we help educate folks into seeing it as an easy combinator b) what are the with/without fusion cost models of different implementations? c) is it useful? I’m slightly inclined to support inclusion. One question I have is whether it’s definable via foldmap itself ? On Wed, May 8, 2019 at 12:36 AM David Feuer wrote: > On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: > >> Hi David, >> >> At the risk of invoking the gods of Language Blorp, I will note that as a >> working programmer I know exactly what Applicative, Traversable, and Monoid >> are (from Vanessa's original proposal), but the unfortunately-named getAp >> is something I will only learn about begrudgingly. >> > > That seems unfortunate. Learning to use such types is pretty useful. I'd > recommend that every Haskell programmer get to know all the types in > Data.Monoid and come to an understanding of what they're good for. > >> > >> What you consider "so simple we don't need to define it" took a rather >> lengthy email to describe. Are you sure it's not worth actually defining? >> > > So ... that long post was about trying to prove what I intuitively thought > *must* be true. In the end, I wasn't quite able to finish the proof, but I > did at least manage to convince myself that my intuition was correct. It's > true that this sort of intuition takes a certain amount of time to develop. > In the case of a really important operation, yeah, we should package it up. > But is this operation important enough? I'm not really convinced yet. > > > If nothing else, the next time someone searches Hoogle for a function >> matching its type signature, perhaps it will be an opportunity for someone >> like me to peer beneath the hood and learn something new. >> > > That's valid. But ... there are lots of opportunities for that sort of > thing already. Is it worth the API clutter to add another one? > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Thu May 9 14:36:34 2019 From: david.feuer at gmail.com (David Feuer) Date: Thu, 9 May 2019 10:36:34 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: Vanessa, it's very common for discussions about adding things to base (or containers, etc) to get into how those matters have been handled elsewhere. Many people think that it's better to pull ideas into base only after they've demonstrated their value elsewhere. Dmitriy's comment should definitely not be taken as criticism of your proposal. Dmitriy, there's no question that (#.) or similar should be used rather than (.). I didn't mention that because it doesn't seem to bear on the overall question of whether the function should be added, and I didn't want to confuse other participants with semi-obscure coercion operators. On Thu, May 9, 2019, 10:28 AM Vanessa McHale wrote: > I appreciate the more efficient version, but I do not consider a package > like relude to be an alternative to base (due to dependencies, maintenance). > On 5/8/19 10:50 PM, Dmitriy Kovanikov wrote: > > I would like to add one more point of reference to the discussion. The > `foldMapA` function is also implemented in the `relude` alternative > prelude: > > > http://hackage.haskell.org/package/relude-0.5.0/docs/src/Relude.Foldable.Fold.html#foldMapA > > And the implementation already uses `Ap` and `getAp` as was discussed > here. Previous implementation used `fmap` and `traverse` but it was changed > to a more efficient one. > > One possible improvement: instead of current implementation > > > foldMapA f = getAp . foldMap (Ap . f) > > It can be slightly more efficient (I guess) by using #. operator to coerce > newtypes > > > foldMapA f = getAp #. foldMap (Ap . f) > > The implementation in `relude` also contains recommended order of > variables under `forall`. After using `foldMapA` in production for a while > we've figured out in what order variables should go to resolve most often > ambiguities via TypeApplication. > > On Wed, May 8, 2019 at 12:36 PM David Feuer wrote: > >> On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: >> >>> Hi David, >>> >>> At the risk of invoking the gods of Language Blorp, I will note that as >>> a working programmer I know exactly what Applicative, Traversable, and >>> Monoid are (from Vanessa's original proposal), but the unfortunately-named >>> getAp is something I will only learn about begrudgingly. >>> >> >> That seems unfortunate. Learning to use such types is pretty useful. I'd >> recommend that every Haskell programmer get to know all the types in >> Data.Monoid and come to an understanding of what they're good for. >> >> >>> What you consider "so simple we don't need to define it" took a rather >>> lengthy email to describe. Are you sure it's not worth actually defining? >>> >> >> So ... that long post was about trying to prove what I intuitively >> thought *must* be true. In the end, I wasn't quite able to finish the >> proof, but I did at least manage to convince myself that my intuition was >> correct. It's true that this sort of intuition takes a certain amount of >> time to develop. In the case of a really important operation, yeah, we >> should package it up. But is this operation important enough? I'm not >> really convinced yet. >> >> >> If nothing else, the next time someone searches Hoogle for a function >>> matching its type signature, perhaps it will be an opportunity for someone >>> like me to peer beneath the hood and learn something new. >>> >> >> That's valid. But ... there are lots of opportunities for that sort of >> thing already. Is it worth the API clutter to add another one? >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > > _______________________________________________ > Libraries mailing listLibraries at haskell.orghttp://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Thu May 9 14:37:53 2019 From: david.feuer at gmail.com (David Feuer) Date: Thu, 9 May 2019 10:37:53 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: Carter, I already showed that it is, and Dmitriy already refined that definition. On Thu, May 9, 2019, 10:35 AM Carter Schonwald wrote: > Its a complicated landscape, and we're still learning. > > if a new combinator is hard to write: > a) how do we help educate folks into seeing it as an easy combinator > b) what are the with/without fusion cost models of different > implementations? > c) is it useful? > > I’m slightly inclined to support inclusion. > > One question I have is whether it’s definable via foldmap itself ? > > On Wed, May 8, 2019 at 12:36 AM David Feuer wrote: > >> On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: >> >>> Hi David, >>> >>> At the risk of invoking the gods of Language Blorp, I will note that as >>> a working programmer I know exactly what Applicative, Traversable, and >>> Monoid are (from Vanessa's original proposal), but the unfortunately-named >>> getAp is something I will only learn about begrudgingly. >>> >> >> That seems unfortunate. Learning to use such types is pretty useful. I'd >> recommend that every Haskell programmer get to know all the types in >> Data.Monoid and come to an understanding of what they're good for. >> >>> >> >>> What you consider "so simple we don't need to define it" took a rather >>> lengthy email to describe. Are you sure it's not worth actually defining? >>> >> >> So ... that long post was about trying to prove what I intuitively >> thought *must* be true. In the end, I wasn't quite able to finish the >> proof, but I did at least manage to convince myself that my intuition was >> correct. It's true that this sort of intuition takes a certain amount of >> time to develop. In the case of a really important operation, yeah, we >> should package it up. But is this operation important enough? I'm not >> really convinced yet. >> >> >> If nothing else, the next time someone searches Hoogle for a function >>> matching its type signature, perhaps it will be an opportunity for someone >>> like me to peer beneath the hood and learn something new. >>> >> >> That's valid. But ... there are lots of opportunities for that sort of >> thing already. Is it worth the API clutter to add another one? >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From kovanikov at gmail.com Thu May 9 14:47:21 2019 From: kovanikov at gmail.com (Dmitriy Kovanikov) Date: Thu, 9 May 2019 22:47:21 +0800 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: Vanessa, I'm not proposing to replace base with relude in libraries like dir-traverse. Sorry if I wasn't clear enough, I didn't mean to introduce more confusion. I don't use `relude` in my libraries as well because I prefer a low amount of external dependencies and I usually use alternative prelude only in big applications. As David mentioned, I indeed just wanted to show one more place where `foldMapA` is introduced because it's not in base. Having `foldMapA` in base will make `relude` even better because it will have even fewer custom things in favour of mere reexports from base. I also think that if some data type / function has already been in some alternative prelude for some time, it could be a good addition to base. On Thu, May 9, 2019 at 10:28 PM Vanessa McHale wrote: > I appreciate the more efficient version, but I do not consider a package > like relude to be an alternative to base (due to dependencies, maintenance). > On 5/8/19 10:50 PM, Dmitriy Kovanikov wrote: > > I would like to add one more point of reference to the discussion. The > `foldMapA` function is also implemented in the `relude` alternative > prelude: > > > http://hackage.haskell.org/package/relude-0.5.0/docs/src/Relude.Foldable.Fold.html#foldMapA > > And the implementation already uses `Ap` and `getAp` as was discussed > here. Previous implementation used `fmap` and `traverse` but it was changed > to a more efficient one. > > One possible improvement: instead of current implementation > > > foldMapA f = getAp . foldMap (Ap . f) > > It can be slightly more efficient (I guess) by using #. operator to coerce > newtypes > > > foldMapA f = getAp #. foldMap (Ap . f) > > The implementation in `relude` also contains recommended order of > variables under `forall`. After using `foldMapA` in production for a while > we've figured out in what order variables should go to resolve most often > ambiguities via TypeApplication. > > On Wed, May 8, 2019 at 12:36 PM David Feuer wrote: > >> On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: >> >>> Hi David, >>> >>> At the risk of invoking the gods of Language Blorp, I will note that as >>> a working programmer I know exactly what Applicative, Traversable, and >>> Monoid are (from Vanessa's original proposal), but the unfortunately-named >>> getAp is something I will only learn about begrudgingly. >>> >> >> That seems unfortunate. Learning to use such types is pretty useful. I'd >> recommend that every Haskell programmer get to know all the types in >> Data.Monoid and come to an understanding of what they're good for. >> >> >>> What you consider "so simple we don't need to define it" took a rather >>> lengthy email to describe. Are you sure it's not worth actually defining? >>> >> >> So ... that long post was about trying to prove what I intuitively >> thought *must* be true. In the end, I wasn't quite able to finish the >> proof, but I did at least manage to convince myself that my intuition was >> correct. It's true that this sort of intuition takes a certain amount of >> time to develop. In the case of a really important operation, yeah, we >> should package it up. But is this operation important enough? I'm not >> really convinced yet. >> >> >> If nothing else, the next time someone searches Hoogle for a function >>> matching its type signature, perhaps it will be an opportunity for someone >>> like me to peer beneath the hood and learn something new. >>> >> >> That's valid. But ... there are lots of opportunities for that sort of >> thing already. Is it worth the API clutter to add another one? >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > > _______________________________________________ > Libraries mailing listLibraries at haskell.orghttp://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From parsonsmatt at gmail.com Thu May 9 15:09:13 2019 From: parsonsmatt at gmail.com (Matt) Date: Thu, 9 May 2019 09:09:13 -0600 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: I've personally defined `foldMapA` in at least three private projects, and I've one-off written it probably over a dozen times. Each time I've used something like `fmap k . traverse f` where `k` is one of `mconcat`, `fold`, `join`, etc. I appreciate the subtle discussion on the implementation for performance and I think it'd be awesome to have this defined in `base`. Matt Parsons On Tue, May 7, 2019 at 10:36 PM David Feuer wrote: > On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: > >> Hi David, >> >> At the risk of invoking the gods of Language Blorp, I will note that as a >> working programmer I know exactly what Applicative, Traversable, and Monoid >> are (from Vanessa's original proposal), but the unfortunately-named getAp >> is something I will only learn about begrudgingly. >> > > That seems unfortunate. Learning to use such types is pretty useful. I'd > recommend that every Haskell programmer get to know all the types in > Data.Monoid and come to an understanding of what they're good for. > >> > >> What you consider "so simple we don't need to define it" took a rather >> lengthy email to describe. Are you sure it's not worth actually defining? >> > > So ... that long post was about trying to prove what I intuitively thought > *must* be true. In the end, I wasn't quite able to finish the proof, but I > did at least manage to convince myself that my intuition was correct. It's > true that this sort of intuition takes a certain amount of time to develop. > In the case of a really important operation, yeah, we should package it up. > But is this operation important enough? I'm not really convinced yet. > > > If nothing else, the next time someone searches Hoogle for a function >> matching its type signature, perhaps it will be an opportunity for someone >> like me to peer beneath the hood and learn something new. >> > > That's valid. But ... there are lots of opportunities for that sort of > thing already. Is it worth the API clutter to add another one? > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From chessai1996 at gmail.com Thu May 9 17:49:50 2019 From: chessai1996 at gmail.com (chessai .) Date: Thu, 9 May 2019 13:49:50 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: I've also defined this in multiple of my own projects/codebases, and I provided it as a motivation for introducing Data.Monoid.Ap in the first place. I'm +1 on the inclusion of foldMapA. On Thu, May 9, 2019, 11:10 AM Matt wrote: > I've personally defined `foldMapA` in at least three private projects, and > I've one-off written it probably over a dozen times. Each time I've used > something like `fmap k . traverse f` where `k` is one of `mconcat`, `fold`, > `join`, etc. I appreciate the subtle discussion on the implementation for > performance and I think it'd be awesome to have this defined in `base`. > > Matt Parsons > > > On Tue, May 7, 2019 at 10:36 PM David Feuer wrote: > >> On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: >> >>> Hi David, >>> >>> At the risk of invoking the gods of Language Blorp, I will note that as >>> a working programmer I know exactly what Applicative, Traversable, and >>> Monoid are (from Vanessa's original proposal), but the unfortunately-named >>> getAp is something I will only learn about begrudgingly. >>> >> >> That seems unfortunate. Learning to use such types is pretty useful. I'd >> recommend that every Haskell programmer get to know all the types in >> Data.Monoid and come to an understanding of what they're good for. >> >>> >> >>> What you consider "so simple we don't need to define it" took a rather >>> lengthy email to describe. Are you sure it's not worth actually defining? >>> >> >> So ... that long post was about trying to prove what I intuitively >> thought *must* be true. In the end, I wasn't quite able to finish the >> proof, but I did at least manage to convince myself that my intuition was >> correct. It's true that this sort of intuition takes a certain amount of >> time to develop. In the case of a really important operation, yeah, we >> should package it up. But is this operation important enough? I'm not >> really convinced yet. >> >> >> If nothing else, the next time someone searches Hoogle for a function >>> matching its type signature, perhaps it will be an opportunity for someone >>> like me to peer beneath the hood and learn something new. >>> >> >> That's valid. But ... there are lots of opportunities for that sort of >> thing already. Is it worth the API clutter to add another one? >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From vanessa.mchale at iohk.io Thu May 9 18:56:38 2019 From: vanessa.mchale at iohk.io (Vanessa McHale) Date: Thu, 9 May 2019 13:56:38 -0500 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: Interestingly, in the case of dir-traverse, foldMapA = (fmap fold .) . traverse ends up being faster than foldMapA f = getAp . foldMap (Ap . f) ...which I did not expect. I suppose we should benchmark this before adding it to base. Cheers, Vanessa McHale On 5/9/19 12:49 PM, chessai . wrote: > I've also defined this in multiple of my own projects/codebases, and I > provided it as a motivation for introducing Data.Monoid.Ap in the > first place. > > I'm +1 on the inclusion of foldMapA. > > On Thu, May 9, 2019, 11:10 AM Matt > wrote: > > I've personally defined `foldMapA` in at least three private > projects, and I've one-off written it probably over a dozen times. > Each time I've used something like `fmap k . traverse f` where `k` > is one of `mconcat`, `fold`, `join`, etc. I appreciate the subtle > discussion on the implementation for performance and I think it'd > be awesome to have this defined in `base`. > > Matt Parsons > > > On Tue, May 7, 2019 at 10:36 PM David Feuer > wrote: > > On Wed, May 8, 2019, 12:12 AM Bryan Richter > wrote: > > Hi David, > > At the risk of invoking the gods of Language Blorp, I will > note that as a working programmer I know exactly what > Applicative, Traversable, and Monoid are (from Vanessa's > original proposal), but the unfortunately-named getAp is > something I will only learn about begrudgingly. > > > That seems unfortunate. Learning to use such types is pretty > useful. I'd recommend that every Haskell programmer get to > know all the types in Data.Monoid and come to an understanding > of what they're good for. > > > What you consider "so simple we don't need to define it" > took a rather lengthy email to describe. Are you sure it's > not worth actually defining? > > > So ... that long post was about trying to prove what I > intuitively thought *must* be true. In the end, I wasn't quite > able to finish the proof, but I did at least manage to > convince myself that my intuition was correct. It's true that > this sort of intuition takes a certain amount of time to > develop. In the case of a really important operation, yeah, we > should package it up. But is this operation important enough? > I'm not really convinced yet. > > > If nothing else, the next time someone searches Hoogle for > a function matching its type signature, perhaps it will be > an opportunity for someone like me to peer beneath the > hood and learn something new. > > > That's valid. But ... there are lots of opportunities for that > sort of thing already. Is it worth the API clutter to add > another one? > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From david.feuer at gmail.com Thu May 9 19:22:07 2019 From: david.feuer at gmail.com (David Feuer) Date: Thu, 9 May 2019 15:22:07 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: That's ... surprising. I'm quite curious what exactly you tried. Did you compile with optimizations? I see that in the Hackage version, foldMapA is defined in a where clause of a recursive function. I wouldn't be surprised if that could cause some trouble, especially if optimizations are disabled. Side note: I doubt you're actually winning anything significant by having a special case for []. On Thu, May 9, 2019, 2:56 PM Vanessa McHale wrote: > Interestingly, in the case of dir-traverse, > > foldMapA = (fmap fold .) . traverse > > ends up being faster than > > foldMapA f = getAp . foldMap (Ap . f) > > ...which I did not expect. I suppose we should benchmark this before > adding it to base. > > Cheers, > Vanessa McHale > On 5/9/19 12:49 PM, chessai . wrote: > > I've also defined this in multiple of my own projects/codebases, and I > provided it as a motivation for introducing Data.Monoid.Ap in the first > place. > > I'm +1 on the inclusion of foldMapA. > > On Thu, May 9, 2019, 11:10 AM Matt wrote: > >> I've personally defined `foldMapA` in at least three private projects, >> and I've one-off written it probably over a dozen times. Each time I've >> used something like `fmap k . traverse f` where `k` is one of `mconcat`, >> `fold`, `join`, etc. I appreciate the subtle discussion on the >> implementation for performance and I think it'd be awesome to have this >> defined in `base`. >> >> Matt Parsons >> >> >> On Tue, May 7, 2019 at 10:36 PM David Feuer >> wrote: >> >>> On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: >>> >>>> Hi David, >>>> >>>> At the risk of invoking the gods of Language Blorp, I will note that as >>>> a working programmer I know exactly what Applicative, Traversable, and >>>> Monoid are (from Vanessa's original proposal), but the unfortunately-named >>>> getAp is something I will only learn about begrudgingly. >>>> >>> >>> That seems unfortunate. Learning to use such types is pretty useful. I'd >>> recommend that every Haskell programmer get to know all the types in >>> Data.Monoid and come to an understanding of what they're good for. >>> >>> >>>> What you consider "so simple we don't need to define it" took a rather >>>> lengthy email to describe. Are you sure it's not worth actually defining? >>>> >>> >>> So ... that long post was about trying to prove what I intuitively >>> thought *must* be true. In the end, I wasn't quite able to finish the >>> proof, but I did at least manage to convince myself that my intuition was >>> correct. It's true that this sort of intuition takes a certain amount of >>> time to develop. In the case of a really important operation, yeah, we >>> should package it up. But is this operation important enough? I'm not >>> really convinced yet. >>> >>> >>> If nothing else, the next time someone searches Hoogle for a function >>>> matching its type signature, perhaps it will be an opportunity for someone >>>> like me to peer beneath the hood and learn something new. >>>> >>> >>> That's valid. But ... there are lots of opportunities for that sort of >>> thing already. Is it worth the API clutter to add another one? >>> _______________________________________________ >>> Libraries mailing list >>> Libraries at haskell.org >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > > _______________________________________________ > Libraries mailing listLibraries at haskell.orghttp://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From david.feuer at gmail.com Fri May 10 13:13:57 2019 From: david.feuer at gmail.com (David Feuer) Date: Fri, 10 May 2019 09:13:57 -0400 Subject: Arg has unlawful instances Message-ID: Foldable, Traversable, Bifoldable, and Bitraversable instances of Data.Semigroup.Arg don't respect ==. For example, Arg () 1 == Arg () 2 = True, but sum (Arg () 1) = 1 and sum (Arg () 2) = 2. I believe the best solution is to remove the Traversable and Bitraversable instances, and define foldMap _ _ = mempty bifoldMap f _ (Arg a _) = a There also needs to be some documentation about the fact that the Arg constructor allows inspection that does not respect Eq. -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Fri May 10 23:30:10 2019 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Sat, 11 May 2019 01:30:10 +0200 (CEST) Subject: Arg has unlawful instances In-Reply-To: References: Message-ID: On Fri, 10 May 2019, David Feuer wrote: > There also needs to be some documentation about the fact that the Arg > constructor allows inspection that does not respect Eq. This follows from Arg's purpose. From ekmett at gmail.com Sat May 11 07:35:18 2019 From: ekmett at gmail.com (Edward Kmett) Date: Sat, 11 May 2019 17:35:18 +1000 Subject: Arg has unlawful instances In-Reply-To: References: Message-ID: -1 I agree with Henning on this one. (==) provides an equivalence relation. Despite the addition of some vocabulary in base 4.12 about how (==) "should" be structural, that is at odds with Arg's actual purpose. I'd rather argue that the attempted refinement of (==)'s documentation was rather overzealous than that Arg as it is defined is wrong. The instances are useful and follow the intent of the classes, just not the extra paragraph that was bolted on sideways to the text describing Eq. -Edward On Sat, May 11, 2019 at 9:30 AM Henning Thielemann < lemming at henning-thielemann.de> wrote: > > On Fri, 10 May 2019, David Feuer wrote: > > > There also needs to be some documentation about the fact that the Arg > > constructor allows inspection that does not respect Eq. > > This follows from Arg's purpose. > -------------- next part -------------- An HTML attachment was scrubbed... URL: From chessai1996 at gmail.com Sat May 11 14:32:06 2019 From: chessai1996 at gmail.com (chessai .) Date: Sat, 11 May 2019 10:32:06 -0400 Subject: Arg has unlawful instances In-Reply-To: References: Message-ID: In what way is the documentation for Eq (as of base 4.12) overzealous, and how would you suggest it be changed? Thanks On Sat, May 11, 2019, 3:35 AM Edward Kmett wrote: > -1 > > I agree with Henning on this one. > > (==) provides an equivalence relation. > > Despite the addition of some vocabulary in base 4.12 about how (==) > "should" be structural, that is at odds with Arg's actual purpose. > > I'd rather argue that the attempted refinement of (==)'s documentation was > rather overzealous than that Arg as it is defined is wrong. > > The instances are useful and follow the intent of the classes, just not > the extra paragraph that was bolted on sideways to the text describing Eq. > > -Edward > > On Sat, May 11, 2019 at 9:30 AM Henning Thielemann < > lemming at henning-thielemann.de> wrote: > >> >> On Fri, 10 May 2019, David Feuer wrote: >> >> > There also needs to be some documentation about the fact that the Arg >> > constructor allows inspection that does not respect Eq. >> >> This follows from Arg's purpose. >> > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From parsonsmatt at gmail.com Sat May 11 14:40:59 2019 From: parsonsmatt at gmail.com (Matt) Date: Sat, 11 May 2019 08:40:59 -0600 Subject: Arg has unlawful instances In-Reply-To: References: Message-ID: IMO, requiring substitivity (forall a b. if a == b then forall f. f a == f b) is a pretty onerous condition for Eq as it is used in practice. Usually type class laws only govern how the type class methods interact with each other and with super classes. The substitutivity law places a requirement on the entire publicly exported interface for a type. The `Set` type in containers is in violation of this law as well (with `f = splitRoot`). Laws are primarily sold to developers as a means of refactoring fearlessly - eg i can rewrite `fmap f . fmap g` into `fmap (f . g)` without altering the meaning of the given program. I'm not sure which refactors are possible with the substitutivity law. Matt Parsons On Sat, May 11, 2019 at 8:32 AM chessai . wrote: > In what way is the documentation for Eq (as of base 4.12) overzealous, and > how would you suggest it be changed? > > Thanks > > On Sat, May 11, 2019, 3:35 AM Edward Kmett wrote: > >> -1 >> >> I agree with Henning on this one. >> >> (==) provides an equivalence relation. >> >> Despite the addition of some vocabulary in base 4.12 about how (==) >> "should" be structural, that is at odds with Arg's actual purpose. >> >> I'd rather argue that the attempted refinement of (==)'s documentation >> was rather overzealous than that Arg as it is defined is wrong. >> >> The instances are useful and follow the intent of the classes, just not >> the extra paragraph that was bolted on sideways to the text describing Eq. >> >> -Edward >> >> On Sat, May 11, 2019 at 9:30 AM Henning Thielemann < >> lemming at henning-thielemann.de> wrote: >> >>> >>> On Fri, 10 May 2019, David Feuer wrote: >>> >>> > There also needs to be some documentation about the fact that the Arg >>> > constructor allows inspection that does not respect Eq. >>> >>> This follows from Arg's purpose. >>> >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From carter.schonwald at gmail.com Sat May 11 16:35:38 2019 From: carter.schonwald at gmail.com (Carter Schonwald) Date: Sat, 11 May 2019 12:35:38 -0400 Subject: Proposal: add foldMapA to Data.Foldable or Control.Applicative In-Reply-To: References: Message-ID: derp, the Ap newtype for getting a monoid from aplicative f over monoid On Thu, May 9, 2019 at 10:38 AM David Feuer wrote: > Carter, I already showed that it is, and Dmitriy already refined that > definition. > > On Thu, May 9, 2019, 10:35 AM Carter Schonwald > wrote: > >> Its a complicated landscape, and we're still learning. >> >> if a new combinator is hard to write: >> a) how do we help educate folks into seeing it as an easy combinator >> b) what are the with/without fusion cost models of different >> implementations? >> c) is it useful? >> >> I’m slightly inclined to support inclusion. >> >> One question I have is whether it’s definable via foldmap itself ? >> >> On Wed, May 8, 2019 at 12:36 AM David Feuer >> wrote: >> >>> On Wed, May 8, 2019, 12:12 AM Bryan Richter wrote: >>> >>>> Hi David, >>>> >>>> At the risk of invoking the gods of Language Blorp, I will note that as >>>> a working programmer I know exactly what Applicative, Traversable, and >>>> Monoid are (from Vanessa's original proposal), but the unfortunately-named >>>> getAp is something I will only learn about begrudgingly. >>>> >>> >>> That seems unfortunate. Learning to use such types is pretty useful. I'd >>> recommend that every Haskell programmer get to know all the types in >>> Data.Monoid and come to an understanding of what they're good for. >>> >>>> >>> >>>> What you consider "so simple we don't need to define it" took a rather >>>> lengthy email to describe. Are you sure it's not worth actually defining? >>>> >>> >>> So ... that long post was about trying to prove what I intuitively >>> thought *must* be true. In the end, I wasn't quite able to finish the >>> proof, but I did at least manage to convince myself that my intuition was >>> correct. It's true that this sort of intuition takes a certain amount of >>> time to develop. In the case of a really important operation, yeah, we >>> should package it up. But is this operation important enough? I'm not >>> really convinced yet. >>> >>> >>> If nothing else, the next time someone searches Hoogle for a function >>>> matching its type signature, perhaps it will be an opportunity for someone >>>> like me to peer beneath the hood and learn something new. >>>> >>> >>> That's valid. But ... there are lots of opportunities for that sort of >>> thing already. Is it worth the API clutter to add another one? >>> _______________________________________________ >>> Libraries mailing list >>> Libraries at haskell.org >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> >> -------------- next part -------------- An HTML attachment was scrubbed... URL: From reedmullanix at gmail.com Mon May 20 01:27:09 2019 From: reedmullanix at gmail.com (Reed Mullanix) Date: Sun, 19 May 2019 18:27:09 -0700 Subject: Proposal: DerivingVia, MTL Message-ID: As it stands, working with newtypes around transformers can be kind of a pain. For example, consider newtype FreshT m a = { unFreshT :: StateT Int m a } deriving (Functor, Applicative, Monad, MonadReader r, ...) Right now, we can't just GND the instance: instance (MonadState s m) => MonadState s (FreshT m) However, the instance we end up writing is completely formulaic. Fortunately, we can solve this by using the recently added -XDerivingVia newtype Inner (t :: (* -> *) -> * -> *) m a = Inner { getInner :: t m a } deriving (Functor, Applicative, Monad) instance (MonadState s m) => MonadState s (Inner (StateT s') m) where get = Inner $ lift get put = Inner . lift . put This lets us derive the instance that we were looking for newtype FreshT m a = FreshT { unFreshT :: StateT Int m a } deriving newtype (Functor, Applicative, Monad, MonadReader r) deriving (MonadState s) via Inner (StateT Int) m This can be extended to other transformers/classes very easily. It also works well when dealing with newtyped transformer stacks. newtype FooT s e m a = FooT { unFooT :: StateT s (WriterT String (ExceptT e m)) a } deriving newtype (Functor, Applicative, Monad, MonadError e) deriving (MonadWriter w) via (StateT s (Inner (WriterT String) (ExceptT e m))) deriving (MonadState s') via Inner (StateT s) (WriterT String (ExceptT e m)) Cheers, Reed Mullanix -------------- next part -------------- An HTML attachment was scrubbed... URL: From chessai1996 at gmail.com Mon May 20 02:57:03 2019 From: chessai1996 at gmail.com (chessai .) Date: Sun, 19 May 2019 22:57:03 -0400 Subject: Proposal: DerivingVia, MTL In-Reply-To: References: Message-ID: I'm confused. This seems more like a helpful tip than a proposal. Where is the proposal? On Sun, May 19, 2019, 9:27 PM Reed Mullanix wrote: > As it stands, working with newtypes around transformers can be kind of a > pain. For example, > consider > > newtype FreshT m a = { unFreshT :: StateT Int m a } > deriving (Functor, Applicative, Monad, MonadReader r, ...) > > Right now, we can't just GND the instance: > > instance (MonadState s m) => MonadState s (FreshT m) > > However, the instance we end up writing is completely formulaic. > Fortunately, we can solve this by using the recently added -XDerivingVia > > newtype Inner (t :: (* -> *) -> * -> *) m a = Inner { getInner :: t m a } > deriving (Functor, Applicative, Monad) > > instance (MonadState s m) => MonadState s (Inner (StateT s') m) where > get = Inner $ lift get > put = Inner . lift . put > > This lets us derive the instance that we were looking for > > newtype FreshT m a = FreshT { unFreshT :: StateT Int m a } > deriving newtype (Functor, Applicative, Monad, MonadReader r) > deriving (MonadState s) via Inner (StateT Int) m > > This can be extended to other transformers/classes very easily. It also > works well when dealing with newtyped transformer stacks. > > newtype FooT s e m a = FooT { unFooT :: StateT s (WriterT String (ExceptT > e m)) a } > deriving newtype (Functor, Applicative, Monad, MonadError e) > deriving (MonadWriter w) via (StateT s (Inner (WriterT String) (ExceptT > e m))) > deriving (MonadState s') via Inner (StateT s) (WriterT String (ExceptT e > m)) > > Cheers, > Reed Mullanix > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From reedmullanix at gmail.com Mon May 20 03:28:50 2019 From: reedmullanix at gmail.com (Reed Mullanix) Date: Sun, 19 May 2019 20:28:50 -0700 Subject: Proposal: DerivingVia, MTL In-Reply-To: References: Message-ID: Sorry, I should've been more clear. The proposal is to add the Inner newtype and it's associated instances to MTL. On Sun, May 19, 2019, 7:57 PM chessai ., wrote: > I'm confused. This seems more like a helpful tip than a proposal. Where is > the proposal? > > On Sun, May 19, 2019, 9:27 PM Reed Mullanix > wrote: > >> As it stands, working with newtypes around transformers can be kind of a >> pain. For example, >> consider >> >> newtype FreshT m a = { unFreshT :: StateT Int m a } >> deriving (Functor, Applicative, Monad, MonadReader r, ...) >> >> Right now, we can't just GND the instance: >> >> instance (MonadState s m) => MonadState s (FreshT m) >> >> However, the instance we end up writing is completely formulaic. >> Fortunately, we can solve this by using the recently added -XDerivingVia >> >> newtype Inner (t :: (* -> *) -> * -> *) m a = Inner { getInner :: t m a } >> deriving (Functor, Applicative, Monad) >> >> instance (MonadState s m) => MonadState s (Inner (StateT s') m) where >> get = Inner $ lift get >> put = Inner . lift . put >> >> This lets us derive the instance that we were looking for >> >> newtype FreshT m a = FreshT { unFreshT :: StateT Int m a } >> deriving newtype (Functor, Applicative, Monad, MonadReader r) >> deriving (MonadState s) via Inner (StateT Int) m >> >> This can be extended to other transformers/classes very easily. It also >> works well when dealing with newtyped transformer stacks. >> >> newtype FooT s e m a = FooT { unFooT :: StateT s (WriterT String (ExceptT >> e m)) a } >> deriving newtype (Functor, Applicative, Monad, MonadError e) >> deriving (MonadWriter w) via (StateT s (Inner (WriterT String) (ExceptT >> e m))) >> deriving (MonadState s') via Inner (StateT s) (WriterT String (ExceptT >> e m)) >> >> Cheers, >> Reed Mullanix >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > -------------- next part -------------- An HTML attachment was scrubbed... URL: From nicolas.frisby at gmail.com Tue May 21 01:48:24 2019 From: nicolas.frisby at gmail.com (Nicolas Frisby) Date: Mon, 20 May 2019 18:48:24 -0700 Subject: request: a Nat ordering constraint that is not an equality constraint Message-ID: This email proposes an extension and possible change to GHC.TypeLits module in the core `base` library. Is there support for this kind of extension/change to GHC.TypeLits? If so, I'll open a GHC GitLab Issue. (If not, I would be very appreciate if someone were able to suggest a nice workaround.) # The Proposed Extension Please add an alternative to <= that is not defined via ~. For example: ``` module GHC.TypeLits.Passive where class IsTrue (a :: Bool) instance IsTrue 'True instance TypeError ... => IsTrue 'False type (<=) x y = IsTrue (x <=? y) ``` as opposed to the existing definition: ``` module GHC.TypeLits where type (<=) x y = (x <=? y) ~ 'True ``` Its name should be an operator and should be somewhat obvious -- I can't think of one other than <=, so I hedged here by putting in a new module instead. There should be a means of *explicitly* converting back and forth between evidence of the two constraints; they are equi-satisfiable but affect type inference in their scope differently. # The Optional Change I anticipate most users would prefer Passive.<= to today's <=, so the optional library change in this proposal is to redefine <= and relegate its old definition to a new name. Perhaps: ``` module GHC.TypeLits where type (<=) x y = IsTrue (x <=? y) type (<=!) x y = (x <=? y) ~ 'True ``` # The Motivation I'll explain with an example for some concreteness. I wrote an interface for size-indexed vectors for my employer. It involves some code like this: ``` import GHC.TypeLits newtype Fin (n :: Nat) = Unsafe_MkFin{forgetFin :: Integer} mkFin :: (KnownNat i,i <= n) => proxy i -> Fin n mkFin = Unsafe_MkFin . natVal ``` Constraints like `(i <= n)` show up throughout the library's interface. The problem is that <= is an equality constraint. Therefore many uses of `mkFin` et al *require* that I introduce local equality constraints. And those sadly spoil lots of desired type inference by making outer tyvars untouchable. That's the problem: I have to choose between GHC's specialized solving of <= constraints and unspoiled type inference in their scope. # Additional Context It is important that today's <= be an equality constraint, because it participates in some constraint improvements that introduce equality constraints. For example (x <= y,y <= x) implies x ~ y (c.f. the scary https://gitlab.haskell.org/ghc/ghc/issues/16586). Because <= constraints have the potential to "become" an equality constraint, tyvars outside of a <= constraint must be untouchable in its scope from the get go. However, neither my library nor any of its uses relies on such constraint improvements enabled by <= constraints. In this context, I would much rather that <= could never "become" an equality constraint, so that it need not be an equality constraint, so that it would never render a tyvar untouchable. # An Alternative As a partial workaround, I could write ``` data Dict i n = (i <= n) => MkDict mkFinD :: (KnownNat i) => Dict i n -> proxy i -> Fin n mkFinD MkDict = mkFin ``` and then take pains to only introduce the <= constraints in the argument of `mkFinD`. By limiting the scope of the ~ constraints like that, I could prevent them from spoiling the desired type inference. But it's very cumbersome to manage their scopes manually. # Work Estimation I just thought of the `IsTrue`-based approach while writing this email, so that detail is somewhat half-baked. It has the right *indicative* semantics, but I doubt today's GHC solver would know what to do with a Given `IsTrue (3 <=? 5)` constraint -- so I'm guessing that exact approach at least would require some significant changes in TcTypeNats. Thank you for your time. -Nick P.S. - Some of the issue tracker links at https://wiki.haskell.org/Library_submissions#The_Libraries respond with 404. P.P.S. - Is there a standard place to find something like `IsTrue`? More generally: a test for type equality that does not drive unification? Thanks again. -------------- next part -------------- An HTML attachment was scrubbed... URL: From rae at richarde.dev Tue May 21 07:28:25 2019 From: rae at richarde.dev (Richard Eisenberg) Date: Tue, 21 May 2019 09:28:25 +0200 Subject: request: a Nat ordering constraint that is not an equality constraint In-Reply-To: References: Message-ID: <609A8C0C-4AB2-4268-BCE1-D2B8956224CB@richarde.dev> This is an interesting proposal. When I started reading it, I wondered why anyone would want to avoid the current definition. But you motivate that part well. I would want a larger test of the IsTrue approach to make sure it does what you want before supporting this. But wait: couldn't you write your GHC.TypeLits.Passive today, in a library, with no ill effect? If so, there isn't a strict reason GHC needs to adopt this. (Of course, if the new definition proves useful, then it might make sense to do so in time.) > On May 21, 2019, at 3:48 AM, Nicolas Frisby wrote: > > P.P.S. - Is there a standard place to find something like `IsTrue`? More generally: a test for type equality that does not drive unification? Thanks again. If something like this ends up in GHC, Data.Type.Bool seems like the right place. Richard From simonpj at microsoft.com Tue May 21 07:43:07 2019 From: simonpj at microsoft.com (Simon Peyton Jones) Date: Tue, 21 May 2019 07:43:07 +0000 Subject: request: a Nat ordering constraint that is not an equality constraint In-Reply-To: References: Message-ID: Therefore many uses of `mkFin` et al *require* that I introduce local equality constraints. And those sadly spoil lots of desired type inference by making outer tyvars untouchable. One would need to think carefully here. For example, just changing (a~b) to (IsEqual a b) doesn’t really make it less of an equality constraint, does it? Yet (IsEqual a b) isn’t an equality constraint, and hence would not make outer type variables untouchable. Does that threaten predictable type inference, as an (a~b) constraint does? I’m not sure. Perhaps if it was defined as class IsEqual a b instance IsEqual a a all would be well. But NOT if you defined it as class (a~b) => IsEqual2 a b instance IsEqual2 a a because of the superclass. Interesting. I’d never thought of that. cc’ing Richard. Simon From: Libraries On Behalf Of Nicolas Frisby Sent: 21 May 2019 02:48 To: Haskell Libraries Subject: request: a Nat ordering constraint that is not an equality constraint This email proposes an extension and possible change to GHC.TypeLits module in the core `base` library. Is there support for this kind of extension/change to GHC.TypeLits? If so, I'll open a GHC GitLab Issue. (If not, I would be very appreciate if someone were able to suggest a nice workaround.) # The Proposed Extension Please add an alternative to <= that is not defined via ~. For example: ``` module GHC.TypeLits.Passive where class IsTrue (a :: Bool) instance IsTrue 'True instance TypeError ... => IsTrue 'False type (<=) x y = IsTrue (x <=? y) ``` as opposed to the existing definition: ``` module GHC.TypeLits where type (<=) x y = (x <=? y) ~ 'True ``` Its name should be an operator and should be somewhat obvious -- I can't think of one other than <=, so I hedged here by putting in a new module instead. There should be a means of *explicitly* converting back and forth between evidence of the two constraints; they are equi-satisfiable but affect type inference in their scope differently. # The Optional Change I anticipate most users would prefer Passive.<= to today's <=, so the optional library change in this proposal is to redefine <= and relegate its old definition to a new name. Perhaps: ``` module GHC.TypeLits where type (<=) x y = IsTrue (x <=? y) type (<=!) x y = (x <=? y) ~ 'True ``` # The Motivation I'll explain with an example for some concreteness. I wrote an interface for size-indexed vectors for my employer. It involves some code like this: ``` import GHC.TypeLits newtype Fin (n :: Nat) = Unsafe_MkFin{forgetFin :: Integer} mkFin :: (KnownNat i,i <= n) => proxy i -> Fin n mkFin = Unsafe_MkFin . natVal ``` Constraints like `(i <= n)` show up throughout the library's interface. The problem is that <= is an equality constraint. Therefore many uses of `mkFin` et al *require* that I introduce local equality constraints. And those sadly spoil lots of desired type inference by making outer tyvars untouchable. That's the problem: I have to choose between GHC's specialized solving of <= constraints and unspoiled type inference in their scope. # Additional Context It is important that today's <= be an equality constraint, because it participates in some constraint improvements that introduce equality constraints. For example (x <= y,y <= x) implies x ~ y (c.f. the scary https://gitlab.haskell.org/ghc/ghc/issues/16586). Because <= constraints have the potential to "become" an equality constraint, tyvars outside of a <= constraint must be untouchable in its scope from the get go. However, neither my library nor any of its uses relies on such constraint improvements enabled by <= constraints. In this context, I would much rather that <= could never "become" an equality constraint, so that it need not be an equality constraint, so that it would never render a tyvar untouchable. # An Alternative As a partial workaround, I could write ``` data Dict i n = (i <= n) => MkDict mkFinD :: (KnownNat i) => Dict i n -> proxy i -> Fin n mkFinD MkDict = mkFin ``` and then take pains to only introduce the <= constraints in the argument of `mkFinD`. By limiting the scope of the ~ constraints like that, I could prevent them from spoiling the desired type inference. But it's very cumbersome to manage their scopes manually. # Work Estimation I just thought of the `IsTrue`-based approach while writing this email, so that detail is somewhat half-baked. It has the right *indicative* semantics, but I doubt today's GHC solver would know what to do with a Given `IsTrue (3 <=? 5)` constraint -- so I'm guessing that exact approach at least would require some significant changes in TcTypeNats. Thank you for your time. -Nick P.S. - Some of the issue tracker links at https://wiki.haskell.org/Library_submissions#The_Libraries respond with 404. P.P.S. - Is there a standard place to find something like `IsTrue`? More generally: a test for type equality that does not drive unification? Thanks again. -------------- next part -------------- An HTML attachment was scrubbed... URL: From rae at richarde.dev Tue May 21 09:54:04 2019 From: rae at richarde.dev (Richard Eisenberg) Date: Tue, 21 May 2019 11:54:04 +0200 Subject: request: a Nat ordering constraint that is not an equality constraint In-Reply-To: References: Message-ID: <0F20B19B-2BE1-46E4-889E-36A31E079361@richarde.dev> I think the idea is, as Nick describes, that an equality constraint approach might make improvement more powerful (i.e. we get more unifications) but has the unfortunate (but necessary) side effect of making variables untouchable. So neither approach is better than the other, and Nick wants to let users choose which one they want. Directly responding to Simon's example: Yes I agree with what you say here. Richard > On May 21, 2019, at 9:43 AM, Simon Peyton Jones via Libraries wrote: > > Therefore many uses of `mkFin` et al *require* that I introduce local equality constraints. And those sadly spoil lots of desired type inference by making outer tyvars untouchable. > > > One would need to think carefully here. For example, just changing (a~b) to (IsEqual a b) doesn’t really make it less of an equality constraint, does it? Yet (IsEqual a b) isn’t an equality constraint, and hence would not make outer type variables untouchable. Does that threaten predictable type inference, as an (a~b) constraint does? I’m not sure. > > Perhaps if it was defined as > class IsEqual a b > instance IsEqual a a > all would be well. But NOT if you defined it as > class (a~b) => IsEqual2 a b > instance IsEqual2 a a > because of the superclass. > > Interesting. I’d never thought of that. cc’ing Richard. > > Simon > > From: Libraries On Behalf Of Nicolas Frisby > Sent: 21 May 2019 02:48 > To: Haskell Libraries > Subject: request: a Nat ordering constraint that is not an equality constraint > > This email proposes an extension and possible change to GHC.TypeLits module in the core `base` library. > > > > Is there support for this kind of extension/change to GHC.TypeLits? If so, I'll open a GHC GitLab Issue. > > > > (If not, I would be very appreciate if someone were able to suggest a nice workaround.) > > > > # The Proposed Extension > > > > Please add an alternative to <= that is not defined via ~. For example: > > > > ``` > > module GHC.TypeLits.Passive where > > > > class IsTrue (a :: Bool) > > instance IsTrue 'True > > instance TypeError ... => IsTrue 'False > > > > type (<=) x y = IsTrue (x <=? y) > > ``` > > > > as opposed to the existing definition: > > > ``` > module GHC.TypeLits where > > type (<=) x y = (x <=? y) ~ 'True > > ``` > > > > Its name should be an operator and should be somewhat obvious -- I can't think of one other than <=, so I hedged here by putting in a new module instead. > > > There should be a means of *explicitly* converting back and forth between evidence of the two constraints; they are equi-satisfiable but affect type inference in their scope differently. > > # The Optional Change > > I anticipate most users would prefer Passive.<= to today's <=, so the optional library change in this proposal is to redefine <= and relegate its old definition to a new name. Perhaps: > > ``` > > module GHC.TypeLits where > > > type (<=) x y = IsTrue (x <=? y) > > type (<=!) x y = (x <=? y) ~ 'True > > ``` > > > > # The Motivation > > > > I'll explain with an example for some concreteness. > > I wrote an interface for size-indexed vectors for my employer. It involves some code like this: > > > > ``` > > import GHC.TypeLits > > > newtype Fin (n :: Nat) = Unsafe_MkFin{forgetFin :: Integer} > > > mkFin :: (KnownNat i,i <= n) => proxy i -> Fin n > mkFin = Unsafe_MkFin . natVal > ``` > > Constraints like `(i <= n)` show up throughout the library's interface. > The problem is that <= is an equality constraint. > > Therefore many uses of `mkFin` et al *require* that I introduce local equality constraints. > > And those sadly spoil lots of desired type inference by making outer tyvars untouchable. > > > > That's the problem: I have to choose between GHC's specialized solving of <= constraints and unspoiled type inference in their scope. > > > > # Additional Context > > > > It is important that today's <= be an equality constraint, because it participates in some constraint improvements that introduce equality constraints. For example (x <= y,y <= x) implies x ~ y (c.f. the scary https://gitlab.haskell.org/ghc/ghc/issues/16586 ). Because <= constraints have the potential to "become" an equality constraint, tyvars outside of a <= constraint must be untouchable in its scope from the get go. > > > However, neither my library nor any of its uses relies on such constraint improvements enabled by <= constraints. > > In this context, I would much rather that <= could never "become" an equality constraint, so that it need not be an equality constraint, so that it would never render a tyvar untouchable. > > # An Alternative > > > As a partial workaround, I could write > > ``` > data Dict i n = (i <= n) => MkDict > > mkFinD :: (KnownNat i) => Dict i n -> proxy i -> Fin n > mkFinD MkDict = mkFin > ``` > > and then take pains to only introduce the <= constraints in the argument of `mkFinD`. By limiting the scope of the ~ constraints like that, I could prevent them from spoiling the desired type inference. But it's very cumbersome to manage their scopes manually. > > # Work Estimation > > > > I just thought of the `IsTrue`-based approach while writing this email, so that detail is somewhat half-baked. It has the right *indicative* semantics, but I doubt today's GHC solver would know what to do with a Given `IsTrue (3 <=? 5)` constraint -- so I'm guessing that exact approach at least would require some significant changes in TcTypeNats. > > > > Thank you for your time. -Nick > > P.S. - Some of the issue tracker links at https://wiki.haskell.org/Library_submissions#The_Libraries respond with 404. > > P.P.S. - Is there a standard place to find something like `IsTrue`? More generally: a test for type equality that does not drive unification? Thanks again. > > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries -------------- next part -------------- An HTML attachment was scrubbed... URL: From nicolas.frisby at gmail.com Tue May 21 15:52:59 2019 From: nicolas.frisby at gmail.com (Nicolas Frisby) Date: Tue, 21 May 2019 08:52:59 -0700 Subject: request: a Nat ordering constraint that is not an equality constraint In-Reply-To: <609A8C0C-4AB2-4268-BCE1-D2B8956224CB@richarde.dev> References: <609A8C0C-4AB2-4268-BCE1-D2B8956224CB@richarde.dev> Message-ID: Yes, it seems possible that a user space declaration of <= via IsTrue as in my first email could get much of the desired behavior. I plan on trying it with the work code base soon, maybe even today -- it'll probably do better than my current workaround. If, however, we want the Nat solver to do anything at all with a Given `IsTrue (n <=? m)`, then I think it will need changes. I don't know that machinery well, but it seems very likely it would ignore such Givens. For example, I would naively expect the Nat solver should discharge a Wanted `IsTrue (n <=? m)` from two Givens `(IsTrue (n <=? x),IsTrue (x <=? m))`. Simon's exploration of IsTrue/IsEqual might shed more light on what exactly the Nat solver should and should not do with such a Given. If it's in fact nothing at all, then yes, maybe a user space solution fully supplants the proposed Passive.<=. But I currently anticipate that it should do something with such Givens. Thanks. -Nick On Tue, May 21, 2019, 00:29 Richard Eisenberg wrote: > This is an interesting proposal. When I started reading it, I wondered why > anyone would want to avoid the current definition. But you motivate that > part well. I would want a larger test of the IsTrue approach to make sure > it does what you want before supporting this. But wait: couldn't you write > your GHC.TypeLits.Passive today, in a library, with no ill effect? If so, > there isn't a strict reason GHC needs to adopt this. (Of course, if the new > definition proves useful, then it might make sense to do so in time.) > > > On May 21, 2019, at 3:48 AM, Nicolas Frisby > wrote: > > > > P.P.S. - Is there a standard place to find something like `IsTrue`? More > generally: a test for type equality that does not drive unification? Thanks > again. > > If something like this ends up in GHC, Data.Type.Bool seems like the right > place. > > Richard -------------- next part -------------- An HTML attachment was scrubbed... URL: From rae at richarde.dev Tue May 21 16:13:48 2019 From: rae at richarde.dev (Richard Eisenberg) Date: Tue, 21 May 2019 18:13:48 +0200 Subject: request: a Nat ordering constraint that is not an equality constraint In-Reply-To: References: <609A8C0C-4AB2-4268-BCE1-D2B8956224CB@richarde.dev> Message-ID: <8B9EE4A1-7FCD-4195-8D9C-F8C951B5F81B@richarde.dev> You're right that, without special support, the IsTrue approach won't work with any deductions from Givens. But -- short of strapping on an SMT solver -- we're always going to fall short there, so we should analyze a particular on-the-ground use case before taking any drastic action. (It sounds like you agree with this.) Richard > On May 21, 2019, at 5:52 PM, Nicolas Frisby wrote: > > Yes, it seems possible that a user space declaration of <= via IsTrue as in my first email could get much of the desired behavior. I plan on trying it with the work code base soon, maybe even today -- it'll probably do better than my current workaround. > > If, however, we want the Nat solver to do anything at all with a Given `IsTrue (n <=? m)`, then I think it will need changes. I don't know that machinery well, but it seems very likely it would ignore such Givens. > > For example, I would naively expect the Nat solver should discharge a Wanted `IsTrue (n <=? m)` from two Givens `(IsTrue (n <=? x),IsTrue (x <=? m))`. > > Simon's exploration of IsTrue/IsEqual might shed more light on what exactly the Nat solver should and should not do with such a Given. If it's in fact nothing at all, then yes, maybe a user space solution fully supplants the proposed Passive.<=. But I currently anticipate that it should do something with such Givens. > > Thanks. -Nick > > On Tue, May 21, 2019, 00:29 Richard Eisenberg > wrote: > This is an interesting proposal. When I started reading it, I wondered why anyone would want to avoid the current definition. But you motivate that part well. I would want a larger test of the IsTrue approach to make sure it does what you want before supporting this. But wait: couldn't you write your GHC.TypeLits.Passive today, in a library, with no ill effect? If so, there isn't a strict reason GHC needs to adopt this. (Of course, if the new definition proves useful, then it might make sense to do so in time.) > > > On May 21, 2019, at 3:48 AM, Nicolas Frisby > wrote: > > > > P.P.S. - Is there a standard place to find something like `IsTrue`? More generally: a test for type equality that does not drive unification? Thanks again. > > If something like this ends up in GHC, Data.Type.Bool seems like the right place. > > Richard -------------- next part -------------- An HTML attachment was scrubbed... URL: From nicolas.frisby at gmail.com Tue May 21 17:18:38 2019 From: nicolas.frisby at gmail.com (Nicolas Frisby) Date: Tue, 21 May 2019 10:18:38 -0700 Subject: request: a Nat ordering constraint that is not an equality constraint In-Reply-To: <8B9EE4A1-7FCD-4195-8D9C-F8C951B5F81B@richarde.dev> References: <609A8C0C-4AB2-4268-BCE1-D2B8956224CB@richarde.dev> <8B9EE4A1-7FCD-4195-8D9C-F8C951B5F81B@richarde.dev> Message-ID: Yep, agreed. Totally practical. FYI: my code base at work seems quite happy with the IsTrue approach; a very happy simplification! Like so: ``` class IsTrue (msg :: ErrorMessage) (b :: Bool) where isTrue :: Proxy# msg -> b :~: 'True instance IsTrue msg 'True where isTrue = \_ -> Refl instance TypeError msg => IsTrue msg 'False isTrue = \_ -> error "impossible" type (<=) n m = IsTrue (Msg n m) (n GHC.TypeLits.<=? m) type Msg (n :: Nat) (m :: Nat) = 'ShowType n ':<>: 'Text " is not <= " ':<>: 'ShowType m ``` (An `absurd` for TypeError might be nice? Via isTrue, I can explicitly convert between the two <= constraints wherever I need to. Which I could use to explicitly manage those hypothetical hypotheticals we were discussing. Just to spell it out: the SMT solver would indeed supercede TcTypeNats, but TcTypeNats in turn already supercedes the IsTrue-based encoding. So I've already lost some <= deductions. But my work code doesn't use any of them in this middle ground -- they're all simple enough that IsTrue works as-is or complicated enough that TcTypeNats didn't work already (I'm grumpily writing and invoking Trusted Code Base "axia" in those cases -- I'm avoiding plugin dependencies at work for now.) On Tue, May 21, 2019, 09:14 Richard Eisenberg wrote: > You're right that, without special support, the IsTrue approach won't work > with any deductions from Givens. But -- short of strapping on an SMT solver > -- we're always going to fall short there, so we should analyze a > particular on-the-ground use case before taking any drastic action. (It > sounds like you agree with this.) > > Richard > > On May 21, 2019, at 5:52 PM, Nicolas Frisby > wrote: > > Yes, it seems possible that a user space declaration of <= via IsTrue as > in my first email could get much of the desired behavior. I plan on trying > it with the work code base soon, maybe even today -- it'll probably do > better than my current workaround. > > If, however, we want the Nat solver to do anything at all with a Given > `IsTrue (n <=? m)`, then I think it will need changes. I don't know that > machinery well, but it seems very likely it would ignore such Givens. > > For example, I would naively expect the Nat solver should discharge a > Wanted `IsTrue (n <=? m)` from two Givens `(IsTrue (n <=? x),IsTrue (x <=? > m))`. > > Simon's exploration of IsTrue/IsEqual might shed more light on what > exactly the Nat solver should and should not do with such a Given. If it's > in fact nothing at all, then yes, maybe a user space solution fully > supplants the proposed Passive.<=. But I currently anticipate that it > should do something with such Givens. > > Thanks. -Nick > > On Tue, May 21, 2019, 00:29 Richard Eisenberg wrote: > >> This is an interesting proposal. When I started reading it, I wondered >> why anyone would want to avoid the current definition. But you motivate >> that part well. I would want a larger test of the IsTrue approach to make >> sure it does what you want before supporting this. But wait: couldn't you >> write your GHC.TypeLits.Passive today, in a library, with no ill effect? If >> so, there isn't a strict reason GHC needs to adopt this. (Of course, if the >> new definition proves useful, then it might make sense to do so in time.) >> >> > On May 21, 2019, at 3:48 AM, Nicolas Frisby >> wrote: >> > >> > P.P.S. - Is there a standard place to find something like `IsTrue`? >> More generally: a test for type equality that does not drive unification? >> Thanks again. >> >> If something like this ends up in GHC, Data.Type.Bool seems like the >> right place. >> >> Richard > > > -------------- next part -------------- An HTML attachment was scrubbed... URL: From simonpj at microsoft.com Wed May 22 11:14:22 2019 From: simonpj at microsoft.com (Simon Peyton Jones) Date: Wed, 22 May 2019 11:14:22 +0000 Subject: request: a Nat ordering constraint that is not an equality constraint In-Reply-To: References: <609A8C0C-4AB2-4268-BCE1-D2B8956224CB@richarde.dev> <8B9EE4A1-7FCD-4195-8D9C-F8C951B5F81B@richarde.dev> Message-ID: I’m afraid I’ve lost sync with this conversation. At some point it’d be good to write up conclusions on a wiki page and/or a ticket, so we don’t lose them. Email threads are hard to parse later. Simon From: Libraries On Behalf Of Nicolas Frisby Sent: 21 May 2019 18:19 To: Richard Eisenberg Cc: Haskell Libraries Subject: Re: request: a Nat ordering constraint that is not an equality constraint Yep, agreed. Totally practical. FYI: my code base at work seems quite happy with the IsTrue approach; a very happy simplification! Like so: ``` class IsTrue (msg :: ErrorMessage) (b :: Bool) where isTrue :: Proxy# msg -> b :~: 'True instance IsTrue msg 'True where isTrue = \_ -> Refl instance TypeError msg => IsTrue msg 'False isTrue = \_ -> error "impossible" type (<=) n m = IsTrue (Msg n m) (n GHC.TypeLits.<=? m) type Msg (n :: Nat) (m :: Nat) = 'ShowType n ':<>: 'Text " is not <= " ':<>: 'ShowType m ``` (An `absurd` for TypeError might be nice? Via isTrue, I can explicitly convert between the two <= constraints wherever I need to. Which I could use to explicitly manage those hypothetical hypotheticals we were discussing. Just to spell it out: the SMT solver would indeed supercede TcTypeNats, but TcTypeNats in turn already supercedes the IsTrue-based encoding. So I've already lost some <= deductions. But my work code doesn't use any of them in this middle ground -- they're all simple enough that IsTrue works as-is or complicated enough that TcTypeNats didn't work already (I'm grumpily writing and invoking Trusted Code Base "axia" in those cases -- I'm avoiding plugin dependencies at work for now.) On Tue, May 21, 2019, 09:14 Richard Eisenberg > wrote: You're right that, without special support, the IsTrue approach won't work with any deductions from Givens. But -- short of strapping on an SMT solver -- we're always going to fall short there, so we should analyze a particular on-the-ground use case before taking any drastic action. (It sounds like you agree with this.) Richard On May 21, 2019, at 5:52 PM, Nicolas Frisby > wrote: Yes, it seems possible that a user space declaration of <= via IsTrue as in my first email could get much of the desired behavior. I plan on trying it with the work code base soon, maybe even today -- it'll probably do better than my current workaround. If, however, we want the Nat solver to do anything at all with a Given `IsTrue (n <=? m)`, then I think it will need changes. I don't know that machinery well, but it seems very likely it would ignore such Givens. For example, I would naively expect the Nat solver should discharge a Wanted `IsTrue (n <=? m)` from two Givens `(IsTrue (n <=? x),IsTrue (x <=? m))`. Simon's exploration of IsTrue/IsEqual might shed more light on what exactly the Nat solver should and should not do with such a Given. If it's in fact nothing at all, then yes, maybe a user space solution fully supplants the proposed Passive.<=. But I currently anticipate that it should do something with such Givens. Thanks. -Nick On Tue, May 21, 2019, 00:29 Richard Eisenberg > wrote: This is an interesting proposal. When I started reading it, I wondered why anyone would want to avoid the current definition. But you motivate that part well. I would want a larger test of the IsTrue approach to make sure it does what you want before supporting this. But wait: couldn't you write your GHC.TypeLits.Passive today, in a library, with no ill effect? If so, there isn't a strict reason GHC needs to adopt this. (Of course, if the new definition proves useful, then it might make sense to do so in time.) > On May 21, 2019, at 3:48 AM, Nicolas Frisby > wrote: > > P.P.S. - Is there a standard place to find something like `IsTrue`? More generally: a test for type equality that does not drive unification? Thanks again. If something like this ends up in GHC, Data.Type.Bool seems like the right place. Richard -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew.thaddeus at gmail.com Fri May 24 17:38:05 2019 From: andrew.thaddeus at gmail.com (Andrew Martin) Date: Fri, 24 May 2019 13:38:05 -0400 Subject: Memory Barriers with STM and MVar Message-ID: I've been poking around in old threads and in the GHC source, and I cannot find this documented anywhere. What are the guarantees that GHC provides around load/store reordering when it comes to using MVar or TVar+retry+atomically. For example: import Data.Primitive import Control.Concurrent.MVar ... foo :: IO () foo = do ... writeByteArray myArr 13 (42 :: Word) putMVar myMVar () If there is another thread that calls takeMVar followed by `readByteArray myArr 13`, is it guaranteed by GHC to see the 42 that's been written to the array. Same question applies for situations using STM facilities to accomplish blocking behavior that MVar gives us. Any documentation or pointers to places in GHC source where these barriers are guaranteed would be appreciated. -- -Andrew Thaddeus Martin -------------- next part -------------- An HTML attachment was scrubbed... URL: From drkoster at qq.com Sat May 25 05:55:50 2019 From: drkoster at qq.com (=?ISO-8859-1?B?RHIuS29zdGVy?=) Date: Sat, 25 May 2019 13:55:50 +0800 Subject: Proposal: add isEmpty to Monoid Message-ID: It's often useful to have a generalized null/unit test, e.g. to check if a monadic stream `m a` reaches EOF. A workaround is to use `Eq a, Monoid a` constraint and use `== mempty` test. But this is no only inefficient for some type, but also impossible for some monoids, such as Builders. I propose add this test `isEmpty :: a -> Bool` to `Monoid`, to provide the ability to check if a value is the unit value. Cheers Han Dong~ -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Sat May 25 06:36:52 2019 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Sat, 25 May 2019 08:36:52 +0200 (CEST) Subject: Proposal: add isEmpty to Monoid In-Reply-To: References: Message-ID: On Sat, 25 May 2019, Dr.Koster wrote: > It's often useful to have a generalized null/unit test, e.g. to check if a monadic stream `m a` reaches EOF.  A > workaround is to use `Eq a, Monoid a` constraint and use `== mempty` test. But this is no only inefficient for > some type, but also impossible for some monoids, such as Builders. > I propose add this test `isEmpty :: a -> Bool` to `Monoid`, to provide the ability to check if a value is the > unit value. This will not work. You cannot always inspect what you have constructed via Monoid. E.g. you will not be able to implement isEmpty for the Endo monoid. The Eq constraint imposed by (== mempty) is really required. If you want isEmpty as method, you need a Monoid sub-class. From simon.jakobi at googlemail.com Sat May 25 06:44:41 2019 From: simon.jakobi at googlemail.com (Simon Jakobi) Date: Sat, 25 May 2019 08:44:41 +0200 Subject: Proposal: add isEmpty to Monoid In-Reply-To: References: Message-ID: The monoid-subclasses packages provides a MonoidNull class that you could use: http://hackage.haskell.org/package/monoid-subclasses-0.4.6.1/docs/Data-Monoid-Null.html Am Sa., 25. Mai 2019 um 07:56 Uhr schrieb Dr.Koster : > It's often useful to have a generalized null/unit test, e.g. to check if a > monadic stream `m a` reaches EOF. A workaround is to use `Eq a, Monoid a` > constraint and use `== mempty` test. But this is no only inefficient for > some type, but also impossible for some monoids, such as Builders. > > I propose add this test `isEmpty :: a -> Bool` to `Monoid`, to provide the > ability to check if a value is the unit value. > > Cheers > Han Dong~ > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From drkoster at qq.com Sat May 25 06:56:07 2019 From: drkoster at qq.com (=?gb18030?B?RHIuS29zdGVy?=) Date: Sat, 25 May 2019 14:56:07 +0800 Subject: Proposal: add isEmpty to Monoid References: Message-ID: I see, Thanks! 发自我的iPhone ------------------ Original ------------------ From: Simon Jakobi Date: Sat,May 25,2019 2:45 PM To: Dr.Koster Cc: libraries Subject: Re: Proposal: add isEmpty to Monoid The monoid-subclasses packages provides a MonoidNull class that you could use: http://hackage.haskell.org/package/monoid-subclasses-0.4.6.1/docs/Data-Monoid-Null.html Am Sa., 25. Mai 2019 um 07:56 Uhr schrieb Dr.Koster : It's often useful to have a generalized null/unit test, e.g. to check if a monadic stream `m a` reaches EOF. A workaround is to use `Eq a, Monoid a` constraint and use `== mempty` test. But this is no only inefficient for some type, but also impossible for some monoids, such as Builders. I propose add this test `isEmpty :: a -> Bool` to `Monoid`, to provide the ability to check if a value is the unit value. Cheers Han Dong~ _______________________________________________ Libraries mailing list Libraries at haskell.org http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries -------------- next part -------------- An HTML attachment was scrubbed... URL: From ndospark320 at gmail.com Mon May 27 05:40:27 2019 From: ndospark320 at gmail.com (Dannyu NDos) Date: Mon, 27 May 2019 14:40:27 +0900 Subject: Add LiftA4 and LiftA5 Message-ID: It just feels so uncomforable that we have LiftM4 and LiftM5, but not Applicative versions of them. -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew.thaddeus at gmail.com Tue May 28 18:54:18 2019 From: andrew.thaddeus at gmail.com (Andrew Martin) Date: Tue, 28 May 2019 14:54:18 -0400 Subject: Add LiftA4 and LiftA5 In-Reply-To: References: Message-ID: I would like to see these added as well. I have found myself wanting them occasionally. On Mon, May 27, 2019 at 1:40 AM Dannyu NDos wrote: > It just feels so uncomforable that we have LiftM4 and LiftM5, but not > Applicative versions of them. > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -- -Andrew Thaddeus Martin -------------- next part -------------- An HTML attachment was scrubbed... URL: From lemming at henning-thielemann.de Tue May 28 19:03:35 2019 From: lemming at henning-thielemann.de (Henning Thielemann) Date: Tue, 28 May 2019 21:03:35 +0200 (CEST) Subject: Add LiftA4 and LiftA5 In-Reply-To: References: Message-ID: On Tue, 28 May 2019, Andrew Martin wrote: > I would like to see these added as well. I have found myself wanting them occasionally. https://hackage.haskell.org/package/utility-ht-0.0.14/docs/Control-Applicative-HT.html#v:lift4 https://hackage.haskell.org/package/utility-ht-0.0.14/docs/Control-Applicative-HT.html#v:lift5 ... intended to be used with qualification like App.lift4 and App.lift5. From andrew.thaddeus at gmail.com Tue May 28 19:15:49 2019 From: andrew.thaddeus at gmail.com (Andrew Martin) Date: Tue, 28 May 2019 15:15:49 -0400 Subject: Memory Barriers with STM and MVar In-Reply-To: References: Message-ID: Perhaps it may be better to open an issue for this information to be added to the GHC user manual. Neither the word "barrier" nor the word "fence" show up in the context of reordering in the user manual. Having some kind of guarantee is extremely important for writing certain types of applications. I suspect that both atomically and all the MVar operations imply a full memory barrier, but I haven't been able this documented anywhere. On Fri, May 24, 2019 at 1:38 PM Andrew Martin wrote: > I've been poking around in old threads and in the GHC source, and I cannot > find this documented anywhere. What are the guarantees that GHC provides > around load/store reordering when it comes to using MVar or > TVar+retry+atomically. For example: > > import Data.Primitive > import Control.Concurrent.MVar > ... > foo :: IO () > foo = do > ... > writeByteArray myArr 13 (42 :: Word) > putMVar myMVar () > > If there is another thread that calls takeMVar followed by `readByteArray > myArr 13`, is it guaranteed by GHC to see the 42 that's been written to the > array. Same question applies for situations using STM facilities to > accomplish blocking behavior that MVar gives us. Any documentation or > pointers to places in GHC source where these barriers are guaranteed would > be appreciated. > > -- > -Andrew Thaddeus Martin > -- -Andrew Thaddeus Martin -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew.thaddeus at gmail.com Tue May 28 19:18:33 2019 From: andrew.thaddeus at gmail.com (Andrew Martin) Date: Tue, 28 May 2019 15:18:33 -0400 Subject: Add LiftA4 and LiftA5 In-Reply-To: References: Message-ID: Thanks. It's good to know somewhere where I can reach for these next time I need them. On Tue, May 28, 2019 at 3:03 PM Henning Thielemann < lemming at henning-thielemann.de> wrote: > > On Tue, 28 May 2019, Andrew Martin wrote: > > > I would like to see these added as well. I have found myself wanting > them occasionally. > > > https://hackage.haskell.org/package/utility-ht-0.0.14/docs/Control-Applicative-HT.html#v:lift4 > > https://hackage.haskell.org/package/utility-ht-0.0.14/docs/Control-Applicative-HT.html#v:lift5 > > ... intended to be used with qualification like App.lift4 and App.lift5. > -- -Andrew Thaddeus Martin -------------- next part -------------- An HTML attachment was scrubbed... URL: From liang.ting.chen.tw at gmail.com Tue May 28 19:35:50 2019 From: liang.ting.chen.tw at gmail.com (Liang-Ting Chen) Date: Tue, 28 May 2019 20:35:50 +0100 Subject: Add LiftA4 and LiftA5 In-Reply-To: References: Message-ID: I think it is more reasonable to adopt idiom brackets proposed in (McBride & Paterson, 2007). For example, you can write ([ Just True || Just False ]) for liftA2 (||) (Just True) (Just False). Oleg has written a blog post on how to use GHC 8.6's Source Plugins to achieve idiom brackets in http://oleg.fi/gists/posts/2018-07-06-idiom-brackets-via-source-pluging.html Cheers, L-T On Mon, 27 May 2019 at 06:40, Dannyu NDos wrote: > It just feels so uncomforable that we have LiftM4 and LiftM5, but not > Applicative versions of them. > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From carter.schonwald at gmail.com Fri May 31 16:52:25 2019 From: carter.schonwald at gmail.com (Carter Schonwald) Date: Fri, 31 May 2019 12:52:25 -0400 Subject: Memory Barriers with STM and MVar In-Reply-To: References: Message-ID: what the precise guarantees are actually may depend on the platform. if you look for the barriers, they're in the RTS c codebase https://github.com/ghc/ghc/blob/bf73419518ca550e85188616f860961c7e2a336b/includes/stg/SMP.h#L24 should get you started, theres also read_barrier and write_barrier in the rts On Tue, May 28, 2019 at 3:16 PM Andrew Martin wrote: > Perhaps it may be better to open an issue for this information to be added > to the GHC user manual. Neither the word "barrier" nor the word "fence" > show up in the context of reordering in the user manual. Having some kind > of guarantee is extremely important for writing certain types of > applications. I suspect that both atomically and all the MVar operations > imply a full memory barrier, but I haven't been able this documented > anywhere. > > On Fri, May 24, 2019 at 1:38 PM Andrew Martin > wrote: > >> I've been poking around in old threads and in the GHC source, and I >> cannot find this documented anywhere. What are the guarantees that GHC >> provides around load/store reordering when it comes to using MVar or >> TVar+retry+atomically. For example: >> >> import Data.Primitive >> import Control.Concurrent.MVar >> ... >> foo :: IO () >> foo = do >> ... >> writeByteArray myArr 13 (42 :: Word) >> putMVar myMVar () >> >> If there is another thread that calls takeMVar followed by `readByteArray >> myArr 13`, is it guaranteed by GHC to see the 42 that's been written to the >> array. Same question applies for situations using STM facilities to >> accomplish blocking behavior that MVar gives us. Any documentation or >> pointers to places in GHC source where these barriers are guaranteed would >> be appreciated. >> >> -- >> -Andrew Thaddeus Martin >> > > > -- > -Andrew Thaddeus Martin > _______________________________________________ > Libraries mailing list > Libraries at haskell.org > http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries > -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew.thaddeus at gmail.com Fri May 31 17:44:15 2019 From: andrew.thaddeus at gmail.com (Andrew Martin) Date: Fri, 31 May 2019 13:44:15 -0400 Subject: Memory Barriers with STM and MVar In-Reply-To: References: Message-ID: Thanks. The does have the information I was looking for. While write_barrier gets used in a number of places, store_load_barrier never actually gets used anywhere. This makes me suspect that MVar and STM cannot be used to coordinate access to memory in this way my example tries to. That is unfortunate. On Fri, May 31, 2019 at 12:52 PM Carter Schonwald < carter.schonwald at gmail.com> wrote: > what the precise guarantees are actually may depend on the platform. > > if you look for the barriers, they're in the RTS c codebase > > https://github.com/ghc/ghc/blob/bf73419518ca550e85188616f860961c7e2a336b/includes/stg/SMP.h#L24 > should get you started, theres also read_barrier and write_barrier in the > rts > > On Tue, May 28, 2019 at 3:16 PM Andrew Martin > wrote: > >> Perhaps it may be better to open an issue for this information to be >> added to the GHC user manual. Neither the word "barrier" nor the word >> "fence" show up in the context of reordering in the user manual. Having >> some kind of guarantee is extremely important for writing certain types of >> applications. I suspect that both atomically and all the MVar operations >> imply a full memory barrier, but I haven't been able this documented >> anywhere. >> >> On Fri, May 24, 2019 at 1:38 PM Andrew Martin >> wrote: >> >>> I've been poking around in old threads and in the GHC source, and I >>> cannot find this documented anywhere. What are the guarantees that GHC >>> provides around load/store reordering when it comes to using MVar or >>> TVar+retry+atomically. For example: >>> >>> import Data.Primitive >>> import Control.Concurrent.MVar >>> ... >>> foo :: IO () >>> foo = do >>> ... >>> writeByteArray myArr 13 (42 :: Word) >>> putMVar myMVar () >>> >>> If there is another thread that calls takeMVar followed by >>> `readByteArray myArr 13`, is it guaranteed by GHC to see the 42 that's been >>> written to the array. Same question applies for situations using STM >>> facilities to accomplish blocking behavior that MVar gives us. Any >>> documentation or pointers to places in GHC source where these barriers are >>> guaranteed would be appreciated. >>> >>> -- >>> -Andrew Thaddeus Martin >>> >> >> >> -- >> -Andrew Thaddeus Martin >> _______________________________________________ >> Libraries mailing list >> Libraries at haskell.org >> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >> > -- -Andrew Thaddeus Martin -------------- next part -------------- An HTML attachment was scrubbed... URL: From andrew.thaddeus at gmail.com Fri May 31 17:46:44 2019 From: andrew.thaddeus at gmail.com (Andrew Martin) Date: Fri, 31 May 2019 13:46:44 -0400 Subject: Memory Barriers with STM and MVar In-Reply-To: References: Message-ID: I feel like what I really need is a primop for sticking a store_load_barrier at an arbitrary place in the code. That would give me the guarantee I'm looking for. Then I could write: foo :: IO () foo = do ... writeByteArray myArr 13 (42 :: Word) storeLoadBarrier putMVar myMVar () On Fri, May 31, 2019 at 1:44 PM Andrew Martin wrote: > Thanks. The does have the information I was looking for. While > write_barrier gets used in a number of places, store_load_barrier never > actually gets used anywhere. This makes me suspect that MVar and STM cannot > be used to coordinate access to memory in this way my example tries to. > That is unfortunate. > > On Fri, May 31, 2019 at 12:52 PM Carter Schonwald < > carter.schonwald at gmail.com> wrote: > >> what the precise guarantees are actually may depend on the platform. >> >> if you look for the barriers, they're in the RTS c codebase >> >> https://github.com/ghc/ghc/blob/bf73419518ca550e85188616f860961c7e2a336b/includes/stg/SMP.h#L24 >> should get you started, theres also read_barrier and write_barrier in the >> rts >> >> On Tue, May 28, 2019 at 3:16 PM Andrew Martin >> wrote: >> >>> Perhaps it may be better to open an issue for this information to be >>> added to the GHC user manual. Neither the word "barrier" nor the word >>> "fence" show up in the context of reordering in the user manual. Having >>> some kind of guarantee is extremely important for writing certain types of >>> applications. I suspect that both atomically and all the MVar operations >>> imply a full memory barrier, but I haven't been able this documented >>> anywhere. >>> >>> On Fri, May 24, 2019 at 1:38 PM Andrew Martin >>> wrote: >>> >>>> I've been poking around in old threads and in the GHC source, and I >>>> cannot find this documented anywhere. What are the guarantees that GHC >>>> provides around load/store reordering when it comes to using MVar or >>>> TVar+retry+atomically. For example: >>>> >>>> import Data.Primitive >>>> import Control.Concurrent.MVar >>>> ... >>>> foo :: IO () >>>> foo = do >>>> ... >>>> writeByteArray myArr 13 (42 :: Word) >>>> putMVar myMVar () >>>> >>>> If there is another thread that calls takeMVar followed by >>>> `readByteArray myArr 13`, is it guaranteed by GHC to see the 42 that's been >>>> written to the array. Same question applies for situations using STM >>>> facilities to accomplish blocking behavior that MVar gives us. Any >>>> documentation or pointers to places in GHC source where these barriers are >>>> guaranteed would be appreciated. >>>> >>>> -- >>>> -Andrew Thaddeus Martin >>>> >>> >>> >>> -- >>> -Andrew Thaddeus Martin >>> _______________________________________________ >>> Libraries mailing list >>> Libraries at haskell.org >>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries >>> >> > > -- > -Andrew Thaddeus Martin > -- -Andrew Thaddeus Martin -------------- next part -------------- An HTML attachment was scrubbed... URL: