Skip to content

See about removing UndecidableInstances #2

@theNerd247

Description

@theNerd247

{-# 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...

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions