packages feed

generic-functor-0.0.1.1: README.md

# Generic functors [![Hackage](https://img.shields.io/hackage/v/generic-functor.svg)](https://hackage.haskell.org/package/generic-functor) [![pipeline status](https://gitlab.com/lysxia/generic-functor/badges/main/pipeline.svg)](https://gitlab.com/lysxia/generic-functor/-/commits/main)


Implementation of `Functor` instances and other functor-like structures
using `GHC.Generics`.

## Functors not over the last type parameter

The standard `Functor` class only applies to types that are functors over their
last type parameter. For example, in `Either e r`, `fmap` maps only `r`.

Using this library, `fmap`-like functions can be derived over any type
parameter of a `Generic` data type, all from the same definition `gsolomap`.

```haskell
{-# LANGUAGE DeriveGeneric #-}

import GHC.Generics (Generic)
import Generic.Functor (gsolomap)

data Result a r = Error a | Ok r   -- Another name for Either
  deriving Generic

mapError :: (a -> b) -> Result a r -> Result b r
mapError = gsolomap

-- This one is fmap
mapOk :: (a -> b) -> Result e a -> Result e b
mapOk = gsolomap

mapBoth :: (a -> b) -> Result a a -> Result b b
mapBoth = gsolomap
```

`gsolomap` is **unsafe**. Misuse will break your program.
Read on for specifics.

### Usage

`gsolomap` should only be used to define **polymorphic** "`fmap`-like functions"
for `Generic` types.

The signature of `gsolomap` is:

```haskell
gsolomap :: (Generic x, Generic y, GSolomap a b x y) => (a -> b) -> (x -> y)
```

The types `x` and `y` must be specializations of the same user-defined `data`
type which is an instance of `Generic`, with some type parameters equal to `a`
or `b` respectively. At use sites of `gsolomap`, `a` and `b` must also be two
distinct universally quantified type variables, with no equality constraint
relating them with each other or any other type.

The guarantee is that `gsolomap` satisfies `gsolomap id = id`. Under the
condition that `a` and `b` are abstract, that equation uniquely determines the
implementation. (That uniqueness claim may be broken with GADTs and other
explicit uses of type equality constraints.)

In particular, `gsolomap` *must not* be specialized with types `a` and `b` that
are equal. A function defined using `gsolomap` is safe to specialize once
the `GSolomap` constraint has been discharged.

For instance the three functions above, `mapError`, `mapOk`, `mapBoth` are
sufficiently polymorphic.
They are each uniquely determined by their types and the equation `mapX id = id`.
(Without that equation, `mapBoth` has four implementations of the same type.)

## Compositions of functors

How many `fmap` do you need to map a function `a -> b` over
`(t, Maybe [Either Bool a])`?

You only need one `solomap`:

```haskell
type F t a = (t, Maybe [Either Bool a])

maps :: (a -> b) -> F t a -> F t b
maps = solomap
```

`solomap` can also see through bifunctors and there may be more than
one occurrence of the type parameter `a`.

```haskell
type F a = ([a], Either a ())

maps2 :: (a -> b) -> F a -> F b
maps2 = solomap
```

`solomap` is **unsafe**, subject to the same restrictions as `gsolomap`:
where `solomap` is used, the type of its first argument `(a -> b)` must refer
to two distinct universally quantified variables `a` and `b`.
Functions are safe to specialize only once the `Solomap` constraint is out of
their contexts.

```haskell
solomap :: Solomap a b x y => (a -> b) -> (x -> y)
```

## Deriving `Functor`

This library enables `DerivingVia` for the `Functor` class.

```haskell
{-# LANGUAGE DeriveGeneric, DerivingVia #-}

import GHC.Generics (Generic)
import Generic.Functor (DeriveFunctor(..))

data Twice a = Twice (Either a a)
  deriving Generic
  deriving Functor via (DeriveFunctor Twice)
```

Note that there is already built-in support for deriving `Functor` in GHC with the
`DeriveFunctor` extension instead. If that extension ever chokes on a type, this
library might have a chance at handling it. (Open an issue if it does not!)

The `Twice` example just above is not handled by the `DeriveFunctor` extension:

```haskell
{-# LANGUAGE DeriveFunctor #-}

data Twice a = Twice (Either a a) deriving Functor

{-
  error:
    • Can't make a derived instance of ‘Functor Twice’:
        Constructor ‘Twice’ must use the type variable only as the last argument of a data type
-}
```

The [*generic-data*][generic-data] library also includes a generic implementation of `Functor`,
but only for instances of `Generic1`, which applies to much more restricted shapes
of `data` than `Generic`.

---

## Internal module policy

The public API is `Generic.Functor`. Don't use `Generic.Functor.Internal`.

## Future work

- Bifunctors and variants with more than one function argument.
- Functors in arbitrary categories.

## Related links

- [*generic-data*][generic-data]

- [*Deriving Bifunctors with Generics*](https://kcsongor.github.io/generic-deriving-bifunctor/),
  blogpost by Csongor Kiss,
  describing the main idea for the implementation (using incoherent instances).

[generic-data]: https://hackage.haskell.org/package/generic-data