snoyberg / mono-traversable

Type classes for mapping, folding, and traversing monomorphic containers
152 stars 61 forks source link

Proposal: newtype wrapper for types with a `Foldable` instance #179

Closed dcastro closed 4 years ago

dcastro commented 4 years ago

I propose adding a newtype that generates a MonoFoldable instance for types that have a Foldable instance.

{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}

newtype FromFoldable f a = FromFoldable (f a)
  deriving newtype Foldable

type instance Element (FromFoldable f a) = a
instance Foldable f => MonoFoldable (FromFoldable f a)

Use Case 1

  1. I'm using a library A that exports a collection data type Coll with a Foldable instance.
    data Coll a = Coll [a]
      deriving Foldable
  2. I want to pass a Coll a to a function from library B that accepts only types with MonoFoldable instances.
    func :: MonoFoldable mono => mono -> IO ()

At the moment, the only solution is to create an orphance instance MonoFoldable (Coll a)

instance MonoFoldable (Coll a)

result = func (Coll [])

With the proposed newtype wrapper, I wouldn't need to create an orphan instance:

result = func (FromFoldable (Coll []))

Use case 2

  1. I'm writing a library, and I export a function that accepts types with MonoFoldable instances
    func :: MonoFoldable mono => mono -> IO ()
  2. I'm aware not every data type my users will be using have a MonoFoldable instance, so I want to also give them the option of passing in a type with a Foldable instance

With the proposed newtype, I could provide both versions of func, without any code duplication, like so:

func :: MonoFoldable mono => mono -> IO ()
func = _

func' :: Foldable f => f a -> IO ()
func' = func . FromFoldable

Notes

The newtype's name (FromFoldable) is up for debate.

I suppose the same rationale applies to:

If the owner/maintainers agree, I'd be happy to open a PR for this.

snoyberg commented 4 years ago

I'm in favor of the addition. One tweak though: do we really need a separate newtype for each typeclass? Couldn't a single newtype Monify f a = Monify (f a) provide all the instances?

Note: Not actually recommending that name :)

dcastro commented 4 years ago

Good point! I suppose that way we could also pass a Monify (Coll xs) into a func :: (MonoFoldable m, MonoFunctor m) => ...

How about naming it ToMono? Or maybe AsMono?

EDIT: I feel inclined towards newtype ToMono f a = ToMono { fromMono :: f a }