Safe Haskell | None |
---|---|
Language | Haskell2010 |
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 :: fString
, age :: fInt
}
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 (Const
String
) -- for the raw input from the web-form, Person (Either
String
) -- for the result of parsing and validating, PersonIdentity
-- for the actual data, Person DbColumn -- To describe how to read / write aPerson
to 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 (Either
String
) ->Either
[String
] (PersonIdentity
)
we can write only one such function:
check ::TraversableB
b => b (Either
String
) ->Either
[String
] (bIdentity
) check be = casebtraverse
(either
(const
Nothing
) (Just
.Identity
)) be ofJust
bi ->Right
biNothing
->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 :: fString
, age :: fInt
} deriving (Generic
,FunctorB
,TraversableB
,ApplicativeB
,ConstraintsB
) deriving instanceAllBF
Show
f Person =>Show
(Person f) deriving instanceAllBF
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
(const
Nothing
)Just
) (p :: Person (Either
e))
In this case, the argument of
bmap
will have to be applied on all
fields of
p
:
name p ::Either
eString
age p ::Either
eInt
So
bmap
here demands a polymorphic function of type:
forall a .Either
e a ->Maybe
a
That is why
bmap
has a rank-2 type:
bmap
::FunctorB
b => (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
:: (TraversableB
b,Applicative
t) => (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
::ApplicativeB
b => (forall a. f a -> g a -> h a) -> b f -> b g -> b h
We can use this to combine barbies:
addDefaults :: PersonMaybe
-> PersonIdentity
-> PersonIdentity
addDefaults =bzipWith
(\m d ->maybe
dpure
m)
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:
Compose
g f aReaderT
r f aMaybeT
f 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
::FunctorT
t => (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
->
Type
MonadT
class
gives us a notion similar to that of
MonadTrans
,
in that it lets us lift a value to its transformed version:
tlift
::MonadT
t => f a -> t f a -- E.g., using the instance for Compose:tlift
[1, 2, 3] =Compose
(Just
[1, 2, 3]) ::Compose
Maybe
[]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),FunctorT
b) => (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),FunctorT
b) => (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
Functor
s.
newtype Container b a Source #
Wrapper for barbies that act as containers of
a
by wearing
(
.
Const
a)
Container | |
|
Instances
newtype ErrorContainer b e Source #
Wrapper for barbies that act as containers of
e
by wearing
.
Either
e
ErrorContainer | |
|
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 # | |
Defined in Data.Barbie.Internal.ProductC |
|
( 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.
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 # | |
Defined in Data.Barbie.Internal.ProductC |
|
Eq ( Unit f) Source # | |
( Typeable f, Typeable k) => Data ( Unit f) Source # | |
Defined in Barbies.Internal.Trivial gfoldl :: ( forall d b. Data d => c (d -> b) -> d -> c b) -> ( forall g. g -> c g) -> Unit f -> c ( Unit f) Source # gunfold :: ( forall b r. Data b => c (b -> r) -> c r) -> ( forall r. r -> c r) -> Constr -> c ( Unit f) Source # toConstr :: Unit f -> Constr Source # dataTypeOf :: Unit f -> DataType Source # dataCast1 :: Typeable t => ( forall d. Data d => c (t d)) -> Maybe (c ( Unit f)) Source # dataCast2 :: Typeable t => ( forall d e. ( Data d, Data e) => c (t d e)) -> Maybe (c ( Unit f)) Source # gmapT :: ( forall b. Data b => b -> b) -> Unit f -> Unit f Source # gmapQl :: (r -> r' -> r) -> r -> ( forall d. Data d => d -> r') -> Unit f -> r Source # gmapQr :: forall r r'. (r' -> r -> r) -> r -> ( forall d. Data d => d -> r') -> Unit f -> r Source # gmapQ :: ( forall d. Data d => d -> u) -> Unit f -> [u] Source # gmapQi :: Int -> ( forall d. Data d => d -> u) -> Unit f -> u Source # gmapM :: Monad m => ( forall d. Data d => d -> m d) -> Unit f -> m ( Unit f) Source # gmapMp :: MonadPlus m => ( forall d. Data d => d -> m d) -> Unit f -> m ( Unit f) Source # gmapMo :: MonadPlus m => ( forall d. Data d => d -> m d) -> Unit f -> m ( Unit f) Source # |
|
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 # | |