|
{-# LANGUAGE UndecidableInstances #-} |
The rumor is, it's unsafe. But it works to get nested Coercable applicative monoids. Here's the scoop: I want to randomly select an element out of a container. If the container is foldable this can be done using foldMap and monoids:
maybeChooseFirst :: (Foldable t) => t a -> Gen (Maybe a)
maybeChooseFirst = fmap getFirst . getAp . foldMap (Ap . fmap First . pickOne)
where
pickOne :: a -> Gen (Maybe a)
pickOne x = elements [Nothing, Just x]
This is done by mapping each element (x) in the structure and to QuickCheck's Gen applicative that selects either Nothing or Just x. The Ap and First are newtype wrappers
to make sure that one the First Just value is extracted and because we're folding and Gen isn't a monoid in its own right we have to piggy pack on Ap which is a monoid wrapper around applicatives.
As you can see pickOne is really the heart of this function but it's surrounded by a bunch of wrapping and unwrapping code to handle the newtypes. However, Newtype from the newtype package was built just for this. However, there's a slight problem. Ap f a unwraps to f a. This isn't an issue if we don't want to unwrap a. But, in our case, a ~ First b needs to unwrap to Maybe b. So we need a Newtype instance (the functor constraint to to ensure we can go up under the f and unwrap the a):
instance (Newtype a b, Functor f) => Newtype (Ap f a) (f b)
which the library doesn't provide. So I had to write my own:
newtype Af f a = Af { unAf :: f a }
deriving (Functor, Applicative)
instance (Applicative f, Monoid m) => Semigroup (Af f m) where
x <> y = Af $ (<>) <$> (coerce x) <*> (coerce y)
instance (Applicative f, Monoid m) => Monoid (Af f m) where
mempty = Af $ pure mempty
instance (Functor f, Newtype a b) => Newtype (Af f a) (f b) where
pack = Af . fmap pack
unpack = fmap unpack . unAf
This requires UndecidableInstances extension. I'm not sure why but the compiler told me so and it's the boss...But! Using this I can coerce my heart's content to wrap and unwrap newtypes. As I think about it I wonder how useful lenses would have been here...
optparse-repline/test/Arbitrary/Repline.hs
Line 7 in 62e032f
The rumor is, it's unsafe. But it works to get nested Coercable applicative monoids. Here's the scoop: I want to randomly select an element out of a container. If the container is foldable this can be done using
foldMapand monoids:This is done by mapping each element (
x) in the structure and to QuickCheck'sGenapplicative that selects eitherNothingorJust x. TheApandFirstare newtype wrappersto make sure that one the
FirstJustvalue is extracted and because we're folding andGenisn't a monoid in its own right we have to piggy pack onApwhich is a monoid wrapper around applicatives.As you can see
pickOneis really the heart of this function but it's surrounded by a bunch of wrapping and unwrapping code to handle the newtypes. However,Newtypefrom thenewtypepackage was built just for this. However, there's a slight problem.Ap f aunwraps tof a. This isn't an issue if we don't want to unwrapa. But, in our case,a ~ First bneeds to unwrap toMaybe b. So we need aNewtypeinstance (the functor constraint to to ensure we can go up under thefand unwrap thea):which the library doesn't provide. So I had to write my own:
This requires
UndecidableInstancesextension. I'm not sure why but the compiler told me so and it's the boss...But! Using this I can coerce my heart's content to wrap and unwrap newtypes. As I think about it I wonder how useful lenses would have been here...