Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

See about removing UndecidableInstances #2

Open
theNerd247 opened this issue Jun 6, 2020 · 1 comment
Open

See about removing UndecidableInstances #2

theNerd247 opened this issue Jun 6, 2020 · 1 comment
Labels
enhancement New feature or request

Comments

@theNerd247
Copy link
Owner

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

@theNerd247 theNerd247 added the enhancement New feature or request label Jun 6, 2020
@theNerd247
Copy link
Owner Author

A simple solution would be to get rid of Af and use coerce instead of the newtype package anywho. Instead I can use a custom newtype whos Semigroup instance handles the logic of randomly selecting between 2 randomly possibly selected values.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant