| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Barbies
Contents
Description
A common Haskell idiom is to parameterise a datatype by a functor or GADT
(or any "indexed type" k -> ), a pattern
sometimes called HKD).
This parameter acts like the outfit of a Barbie, turning it into a different
doll. The canonical example would be:Type
data Person f
= Person
{ name :: f String
, age :: f Int
}
Let's say that we are writing an application where Person data
will be read from a web form, validated, and stored in a database. Some
possibles outfits that we could use along the way are:
Person (ConstString) -- for the raw input from the web-form, Person (EitherString) -- for the result of parsing and validating, PersonIdentity-- for the actual data, Person DbColumn -- To describe how to read / write aPersonto the db data DbColumn a = DbColumn { colName ::String, fromDb :: DbDataParser a , toDb :: a -> DbData }
In such application it is likely that one will have lots of types like
Person so we will like to handle these transformations uniformly,
without boilerplate or repetitions. This package provides classes to
manipulate these types, using notions that are familiar to haskellers like
Functor, Applicative or Traversable. For example, instead of writing
an ad-hoc function that checks that all fields have a correct value, like
checkPerson :: Person (EitherString) ->Either[String] (PersonIdentity)
we can write only one such function:
check ::TraversableBb => b (EitherString) ->Either[String] (bIdentity) check be = casebtraverse(either(constNothing) (Just.Identity)) be ofJustbi ->RightbiNothing->Left(bfoldMap(either(:[]) (const[])) be)
Moreover, these classes come with default instances based on
Generic, so using them is as easy as:
data Person f
= Person
{ name :: f String
, age :: f Int
}
deriving
( Generic
, FunctorB, TraversableB, ApplicativeB, ConstraintsB
)
deriving instance AllBF Show f Person => Show (Person f)
deriving instance AllBF Eq f Person => Eq (Person f)
Synopsis
- module Data.Functor.Barbie
- module Data.Functor.Transformer
- module Barbies.Bi
- newtype Container b a = Container {
- getContainer :: b (Const a)
- newtype ErrorContainer b e = ErrorContainer {
- getErrorContainer :: b (Either e)
- newtype Barbie (b :: (k -> Type) -> Type) f = Barbie {
- getBarbie :: b f
- data Void (f :: k -> Type)
- data Unit (f :: k -> Type) = Unit
Barbies are functors
Barbie-types are functors. That means that if one is familiar
with standard classes like Functor, Applicative or Traversable,
one already knows how to work with barbie-types too. For instance, just
like one would use:
fmap f (as :: [a])
to apply f uniformly on every a occurring
in as, one could use the following to turn a Either-outfit
into Maybe-outfit:
bmap(either(constNothing)Just) (p :: Person (Eithere))
In this case, the argument of bmap will have to be applied on all
fields of p:
name p ::EithereStringage p ::EithereInt
So bmap here demands a polymorphic function of type:
forall a .Eithere a ->Maybea
That is why bmap has a rank-2 type:
bmap::FunctorBb => (forall a. f a -> g a) -> b f -> b g
Polymorphic functions with Applicative effects can be applied
using btraverse and the effects will be accumulated:
btraverse:: (TraversableBb,Applicativet) => (forall a. f a -> t (g a)) -> b f -> t (b g)
Finally, some barbie-types (typically records like Person) have an
Applicative structure, and allow us to lift pure n-ary functions
to functions on barbie-types. For example, bzipWith gives us an analogous
of liftA2:
bzipWith::ApplicativeBb => (forall a. f a -> g a -> h a) -> b f -> b g -> b h
We can use this to combine barbies:
addDefaults :: PersonMaybe-> PersonIdentity-> PersonIdentityaddDefaults =bzipWith(\m d ->maybedpurem)
Why is there not a MonadB class as well? As everyone knows,
a monad is just a monoid in the category of endofunctors,
which in this case is a problem, since barbie-types are not endofunctors:
they map indexed-types to types, unlike the Functor class, that
captures endo-functors on Type.
All these classes, and other convenient functions are found in:
module Data.Functor.Barbie
Transformers are functors
Haskellers may be more used to playing with another family of dolls: transformers. Consider for example the following functor-transformers:
Composeg f aReaderTr f aMaybeTf a
Like with barbies, we can think that different choices of f will
give us a different doll. And if we start thinking about how
to change the outfit of a transformer, we notice that, just like
barbie-types, transformer-types are functors too.
tmap::FunctorTt => (forall a. f a -> g a) -> t f x -> b g x
Where FunctorB captures functors from indexed-types to types,
FunctorT captures those between indexed-types. And again, we can
identitfy familiar classes of functors: ApplicativeT and TraversableT.
Now, transformers like the ones above, are actually endofunctors, e.g.
they map to itself. So it makes
sense to classify those that are actually monads: the Type -> TypeMonadT class
gives us a notion similar to that of MonadTrans,
in that it lets us lift a value to its transformed version:
tlift::MonadTt => f a -> t f a -- E.g., using the instance for Compose:tlift[1, 2, 3] =Compose(Just[1, 2, 3]) ::ComposeMaybe[]Int
Unlike all other classes in this package, MonadT instances need to be written
by hand.
For further details, see:
module Data.Functor.Transformer
Bi-functors and nesting
A barbie-type that is parametric on an additional functor can be made an
instance of both FunctorB and FunctorT. For example:
data B f g = B (f Int) (g Bool) deriving (Generic) instance FunctorB (B f) instance FunctorT B
This gives us a a bifunctor on indexed-types, as we can map
simultaneously over both arguments using btmap:
btmap:: (FunctorB(b f),FunctorTb) => (forall a . f a -> f' a) -> (forall a . g a -> g' a) -> b f g -> b f' g'
When f ~ g, we can use a specialized version of btmap:
btmap1:: (FunctorB(b f),FunctorTb) => (forall a . f a -> f' a) -> b f f -> b f' f'
Functions like btmap1 can be useful to handle cases where we would like
a barbie-type to occur under the functor-argument. Let's consider an example
of this. Continuing the web form example above, one may want to find out
about a person's dependants and model it as follows:
newtype Dependants f
= Dependants { getDependants :: f [Person f] }
This has the appeal of letting us distinguish two states:
Dependants { getDependants = Just [] } -- the user declared 0 dependants
Dependants { getDependants = Nothing } -- the user didn't specify dependants yet
Unfortunately, it is not possible to write a FunctorB instance for such
a type (before going on, try to write one yourself!). Intuitively, we would
need to have , which we can't assume. However, such a type
can be rewritten as follows:Functor f
newtype Dependants f' f
= Dependants { getDependants :: f' [Person f] }
deriving (Generic)
instance Functor f' => FunctorB (Dependants f')
instance FunctorT Dependants
type Dependants f = Dependants f f
We can thus use btmap1 as a poor man's version of bmap for Dependants.
For more details, see:
module Barbies.Bi
Container-barbies
Some clothes make barbies look like containers, and we can make those
types behave like normal Functors.
newtype Container b a Source #
Wrapper for barbies that act as containers of a
by wearing (.Const a)
Constructors
| Container | |
Fields
| |
Instances
newtype ErrorContainer b e Source #
Wrapper for barbies that act as containers of e
by wearing .Either e
Constructors
| ErrorContainer | |
Fields
| |
Instances
Wrappers
This can be use with deriving via to automate derivation of instances for Barbie-types.
newtype Barbie (b :: (k -> Type) -> Type) f Source #
A wrapper for Barbie-types, providing useful instances.
Instances
| FunctorB b => FunctorB (Barbie b :: (k -> Type) -> Type) Source # | |
| TraversableB b => TraversableB (Barbie b :: (k -> Type) -> Type) Source # | |
Defined in Barbies.Internal.Wrappers | |
| ApplicativeB b => ApplicativeB (Barbie b :: (k -> Type) -> Type) Source # | |
| ConstraintsB b => ConstraintsB (Barbie b :: (k -> Type) -> Type) Source # | |
| ProductB b => ProductB (Barbie b :: (k -> Type) -> Type) Source # | |
| ProductBC b => ProductBC (Barbie b :: (k -> Type) -> Type) Source # | |
| (ConstraintsB b, ApplicativeB b, AllBF Semigroup f b) => Semigroup (Barbie b f) Source # | |
| (ConstraintsB b, ApplicativeB b, AllBF Semigroup f b, AllBF Monoid f b) => Monoid (Barbie b f) Source # | |
| type AllB (c :: k -> Constraint) (Barbie b :: (k -> Type) -> Type) Source # | |
Defined in Barbies.Internal.Wrappers | |
Trivial Barbies
data Void (f :: k -> Type) Source #
Uninhabited barbie type.
Instances
| FunctorB (Void :: (k -> Type) -> Type) Source # | |
| TraversableB (Void :: (k -> Type) -> Type) Source # | |
Defined in Barbies.Internal.Trivial | |
| ConstraintsB (Void :: (k -> Type) -> Type) Source # | |
| Eq (Void f) Source # | |
| Ord (Void f) Source # | |
| Show (Void f) Source # | |
| Generic (Void f) Source # | |
| Semigroup (Void f) Source # | |
| type AllB (c :: k -> Constraint) (Void :: (k -> Type) -> Type) Source # | |
| type Rep (Void f) Source # | |
data Unit (f :: k -> Type) Source #
A barbie type without structure.
Constructors
| Unit |
Instances
| FunctorB (Unit :: (k -> Type) -> Type) Source # | |
| TraversableB (Unit :: (k -> Type) -> Type) Source # | |
Defined in Barbies.Internal.Trivial | |
| DistributiveB (Unit :: (k -> Type) -> Type) Source # | |
Defined in Barbies.Internal.Trivial | |
| ApplicativeB (Unit :: (k -> Type) -> Type) Source # | |
| ConstraintsB (Unit :: (k -> Type) -> Type) Source # | |
| ProductB (Unit :: (k -> Type) -> Type) Source # | |
| ProductBC (Unit :: (k -> Type) -> Type) Source # | |
| Eq (Unit f) Source # | |
| (Typeable f, Typeable k) => Data (Unit f) Source # | |
Defined in Barbies.Internal.Trivial Methods gfoldl :: (forall d b. Data d => c (d -> b) -> d -> c b) -> (forall g. g -> c g) -> Unit f -> c (Unit f) # gunfold :: (forall b r. Data b => c (b -> r) -> c r) -> (forall r. r -> c r) -> Constr -> c (Unit f) # toConstr :: Unit f -> Constr # dataTypeOf :: Unit f -> DataType # dataCast1 :: Typeable t => (forall d. Data d => c (t d)) -> Maybe (c (Unit f)) # dataCast2 :: Typeable t => (forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c (Unit f)) # gmapT :: (forall b. Data b => b -> b) -> Unit f -> Unit f # gmapQl :: (r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> Unit f -> r # gmapQr :: (r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> Unit f -> r # gmapQ :: (forall d. Data d => d -> u) -> Unit f -> [u] # gmapQi :: Int -> (forall d. Data d => d -> u) -> Unit f -> u # gmapM :: Monad m => (forall d. Data d => d -> m d) -> Unit f -> m (Unit f) # gmapMp :: MonadPlus m => (forall d. Data d => d -> m d) -> Unit f -> m (Unit f) # gmapMo :: MonadPlus m => (forall d. Data d => d -> m d) -> Unit f -> m (Unit f) # | |
| Ord (Unit f) Source # | |
| Read (Unit f) Source # | |
| Show (Unit f) Source # | |
| Generic (Unit f) Source # | |
| Semigroup (Unit f) Source # | |
| Monoid (Unit f) Source # | |
| type AllB (c :: k -> Constraint) (Unit :: (k -> Type) -> Type) Source # | |
| type Rep (Unit f) Source # | |