Types are great. Lifting them into some sort of applicative functor makes them even better. This post is an homage to our favorite applicatives, and to the semigroups with which they are instrinsically connected.
Lifted-but-why
LiftedButWhy
is a boring functor that just has one value and no other
structure or interesting properties.
data LiftedButWhy a =
-- A value that has been lifted
-- for some damned reason
LiftedButWhy a
deriving (Eq, Functor, Show)
… Okay, to be honest, this one is nobody’s favorite, but it is included here for completeness.
instance Applicative LiftedButWhy where
pure = LiftedButWhy
LiftedButWhy f <*> LiftedButWhy a =
LiftedButWhy (f a)
instance Monad LiftedButWhy where
LiftedButWhy a >>= f = f a
instance Semigroup a =>
Semigroup (LiftedButWhy a) where
LiftedButWhy x <> LiftedButWhy y =
LiftedButWhy (x <> y)
instance Monoid a =>
Monoid (LiftedButWhy a) where
mempty = LiftedButWhy mempty
Or-not
OrNot
is somehow slightly more interesting than LiftedButWhy
, even
though it may actually contain less. Instead of a value, there might not
be a value.
When you combine stuff with (<*>)
or (<>)
, all of the values need to be
present. If any of them are absent, the whole expression evaluates to Nope
.
data OrNot a =
ActuallyYes a -- Some normal value
| Nope -- Chuck Testa
deriving (Eq, Functor, Show)
If you have a function f
that might not actually be there, and a value a
that might not actually be there, lifted application (<*>)
gives you f a
only if both of them are actually there.
instance Applicative OrNot where
pure = ActuallyYes
ActuallyYes f <*> ActuallyYes a =
ActuallyYes (f a)
_ <*> _ = Nope
instance Monad OrNot where
ActuallyYes a >>= f = f a
Nope >>= _ = Nope
If you have value a
that may not actually be there, and another value a'
that might not actually be there, the lifted semigroup operation (<>)
gives
you a <> a'
only if both of them are actually there.
instance Semigroup a =>
Semigroup (OrNot a) where
ActuallyYes a <> ActuallyYes a' =
ActuallyYes (a <> a')
_ <> _ = Nope
instance Monoid a =>
Monoid (OrNot a) where
mempty = ActuallyYes mempty
Two
Two
is two values. Yep. Just two values.
data Two a = Two
{ firstOfTwo :: a -- One value
, secondOfTwo :: a -- Another value
} deriving (Eq, Functor, Show)
If you have two functions f
and g
and two values a
and a'
, then you
can apply them with (<*>)
to get two results f a
and g a'
.
instance Applicative Two where
pure a = Two a a
Two f g <*> Two a a' = Two (f a) (g a')
instance Semigroup a =>
Semigroup (Two a) where
Two x y <> Two x' y' =
Two (x <> x') (y <> y')
instance Monoid a =>
Monoid (Two a) where
mempty = Two mempty mempty
Any-number-of
AnyNumberOf
starts to get exciting. Any number of values you want. Zero …
one … two … three … four … five … The possibilities are truly endless.
data AnyNumberOf a =
-- One value, and maybe even more after that!
OneAndMaybeMore a (AnyNumberOf a)
-- Oh. Well this is less fun.
| ActuallyNone
deriving (Eq, Functor, Show)
Here’s an alias for OneAndMaybeMore
which provides some brevity:
(~~) :: a -> AnyNumberOf a -> AnyNumberOf a
(~~) = OneAndMaybeMore
infixr 5 ~~
You can use the applicative functor to apply any number of functions to any number of arguments.
instance Applicative AnyNumberOf where
pure a = OneAndMaybeMore a ActuallyNone
OneAndMaybeMore f fs <*> OneAndMaybeMore x xs =
OneAndMaybeMore (f x) (fs <*> xs)
_ <*> _ = ActuallyNone
Example:
((+ 1) ~~ (* 2) ~~ ActuallyNone)
<*> ( 1 ~~ 6 ~~ 37 ~~ ActuallyNone)
= ( 7 ~~ 12 ~~ ActuallyNone)
This example demonstrates how when there are more arguments than functions, any
excess arguments (in this case, the 37
) are ignored.
The operation of combining some number of a
with some other number of a
is
sometimes referred to as zipping.
instance Semigroup a =>
Semigroup (AnyNumberOf a) where
OneAndMaybeMore x xs <> OneAndMaybeMore y ys =
OneAndMaybeMore (x <> y) (xs <> ys)
_ <> _ = ActuallyNone
instance Monoid a =>
Monoid (AnyNumberOf a) where
mempty = mempty ~~ mempty
One-or-more
OneOrMore
is more restrictive than AnyNumberOf
, yet somehow actually
more interesting, because it excludes that dull situation where there aren’t
any values at all.
data OneOrMore a = OneOrMore
-- Definitely at least this one
{ theFirstOfMany :: a
-- And perhaps others
, possiblyMore :: AnyNumberOf a
} deriving (Eq, Functor, Show)
instance Applicative OneOrMore where
pure a = OneOrMore a ActuallyNone
OneOrMore f fs <*> OneOrMore x xs =
OneOrMore (f x) (fs <*> xs)
instance Semigroup a =>
Semigroup (OneOrMore a) where
OneOrMore a more <> OneOrMore a' more' =
OneOrMore a (more <> OneAndMaybeMore a' more')
instance Monoid a =>
Monoid (OneOrMore a) where
mempty = OneOrMore mempty ActuallyNone
Also-extra-thing
Also extraThing
is a functor in which each value has an extraThing
of
some other type that tags along with it.
data (Also extraThing) a = Also
-- A value
{ withoutExtraThing :: a
-- An additional thing that tags along
, theExtraThing :: extraThing
} deriving (Eq, Functor, Show)
Dragging the extraThing
along can be a bit of a burden. It prevents Also
extraThing
from being an applicative functor — unless the extraThing
can pull
its weight by bringing a monoid to the table.
instance Monoid extraThing =>
Applicative (Also extraThing) where
pure = (`Also` mempty)
(f `Also` extra1) <*> (a `Also` extra2) =
f a
`Also` (extra1 <> extra2)
instance (Semigroup extraThing, Semigroup a) =>
Semigroup ((Also extraThing) a) where
(a `Also` extra1) <> (a' `Also` extra2) =
(a <> a')
`Also` (extra1 <> extra2)
instance (Monoid extraThing, Monoid a) =>
Monoid ((Also extraThing) a) where
mempty = Also mempty mempty
Or-instead-other-thing
OrInstead otherThing
is a functor in which, instead of having a value, can
actually just have some totally unrelated otherThing
instead.
When you combine stuff with (<*>)
or (<>)
, all of the values need to be
present. If any of them are the otherThing
instead, then the whole expression
evaluates to the combination of the otherThing
s.
data (OrInstead otherThing) a =
-- A normal value
NotInstead a
-- Some totally unrelated other thing
| Instead otherThing
deriving (Eq, Functor, Show)
The possibility of having an otherThing
obstructs this functor’s ability
to be applicative, much like the extra thing in Also extraThing
does. In
this case, since we do not need an empty value for the otherThing
, it needs
only a semigroup to be in compliance.
instance Semigroup otherThing =>
Applicative (OrInstead otherThing) where
pure = NotInstead
NotInstead f <*> NotInstead a =
NotInstead (f a)
Instead other1 <*> Instead other2 =
Instead (other1 <> other2)
Instead other <*> _ = Instead other
_ <*> Instead other = Instead other
instance (Semigroup otherThing, Semigroup a) =>
Semigroup ((OrInstead otherThing) a) where
NotInstead a <> NotInstead a' =
NotInstead (a <> a')
Instead other1 <> Instead other2 =
Instead (other1 <> other2)
Instead other <> _ = Instead other
_ <> Instead other = Instead other
instance (Semigroup otherThing, Monoid a) =>
Monoid ((OrInstead otherThing) a) where
mempty = NotInstead mempty
Or-instead-first-thing
OrInsteadFirst otherThing
looks a lot like OrInstead otherThing
, but it
manages to always be an applicative functor — and even a monad too — by handling
the otherThing
s a bit more hamfistedly.
When you combine stuff with (<*>)
or (<>)
, all of the values need to
be present. If any of them are the otherThing
instead, then the whole
expression evaluates to the first otherThing
encountered, ignoring any
additional otherThing
s that may subsequently pop up.
data (OrInsteadFirst otherThing) a =
-- A normal value
NotInsteadFirst a
-- Some totally unrelated other thing
| InsteadFirst otherThing
deriving (Eq, Functor, Show)
instance Applicative (OrInsteadFirst otherThing) where
pure = NotInsteadFirst
NotInsteadFirst f <*> NotInsteadFirst a =
NotInsteadFirst (f a)
InsteadFirst other <*> _ = InsteadFirst other
_ <*> InsteadFirst other = InsteadFirst other
instance Monad (OrInsteadFirst otherThing) where
InsteadFirst other >>= _ = InsteadFirst other
NotInsteadFirst a >>= f = f a
instance (Semigroup otherThing, Semigroup a) =>
Semigroup ((OrInsteadFirst otherThing) a) where
NotInsteadFirst a <> NotInsteadFirst a' =
NotInsteadFirst (a <> a')
InsteadFirst other <> _ = InsteadFirst other
_ <> InsteadFirst other = InsteadFirst other
instance (Semigroup otherThing, Monoid a) =>
Monoid ((OrInsteadFirst otherThing) a) where
mempty = NotInsteadFirst mempty
Determined-by-parameter
DeterminedBy parameter
is a value that… well, we’re not really sure what
it is. We’ll find out once a parameter
is provided.
The mechanism for deciding how the value is determined from the parameter
is
opaque; all you can do is test it with different parameters and see what
results. There aren’t even Eq
or Show
instances, which is annoying.
data DeterminedBy parameter a =
Determination ((->) parameter a)
deriving Functor
instance Applicative (DeterminedBy parameter) where
pure a = Determination (\_ -> a)
Determination f <*> Determination a =
Determination (\x -> f x (a x))
instance Monad (DeterminedBy parameter) where
Determination fa >>= ff =
Determination (\x ->
let Determination f = ff (fa x)
in f x)
instance Semigroup a =>
Semigroup ((DeterminedBy parameter) a) where
Determination f <> Determination g =
Determination (\x -> f x <> g x)
instance Monoid a =>
Monoid ((DeterminedBy parameter) a) where
mempty = Determination (\_ -> mempty)
Footnotes
LiftedButWhy
is Identity
.
OrNot
is Maybe
, but with the monoid that is appropriate for its applicative.
Two
doesn’t have an analogue in any standard library as far as I know.
AnyNumberOf
is ZipList
, with the appropriate semigroup added.
OneOrMore
is like NonEmpty
, but with instances that match ZipList
.
Also
is (,)
— also known as the 2-tuple.
OrInstead
is AccValidation
from the validation package.
OrInsteadFirst
is Either
.
DeterminedBy
is (->)
, also known as a function, whose monad is also known
as Reader
.
This text is also available on GitHub and as acme-functors on Hackage.