I’m announcing a small utility Haskell package I created that can wrap arbitrary Map
-like types to provide Applicative
and Alternative
instances. You can find this package on Hackage here:
I can motivate why the Applicative
and Alternative
instances matter with a small example. Suppose that I define the following three Map
s which are sort of like database tables:
import Defaultable.Map
firstNames :: Defaultable (Map Int) String
= fromList [(0, "Gabriella"), (1, "Oscar"), (2, "Edgar")]
firstNames
lastNames :: Defaultable (Map Int) String
= fromList [(0, "Gonzalez"), (2, "Codd"), (3, "Bryant")]
lastNames
handles :: Defaultable (Map Int) String
= fromList [(0, "GabriellaG439"), (1, "posco"), (3, "avibryant")] handles
If you squint, you can think of these as analogous to database tables, where the primary key is an Int
index:
> CREATE TABLE firstNames (id integer, firstName text);
> INSERT INTO firstNames (id, firstName) VALUES (0, 'Gabriella');
> INSERT INTO firstNames (id, firstName) VALUES (1, 'Oscar');
> INSERT INTO firstNames (id, firstName) VALUES (2, 'Edgar');
> SELECT * FROM firstNames;
id | firstName
---+----------
0 | Gabriella
1 | Oscar
2 | Edgar
> CREATE TABLE lastNames (id integer, lastName text);
> INSERT INTO lastNames (id, lastName) VALUES (0, 'Gonzalez');
> INSERT INTO lastNames (id, lastName) VALUES (2, 'Codd');
> INSERT INTO lastNames (id, lastName) VALUES (3, 'Bryant');
> SELECT * FROM lastNames;
id | lastName
---+---------
0 | Gonzalez
2 | Codd
3 | Bryant
> CREATE TABLE handles (id integer, handle text);
> INSERT INTO handles (id, handle) VALUES (0, 'GabriellaG439');
> INSERT INTO handles (id, handle) VALUES (1, 'posco');
> INSERT INTO handles (id, handle) VALUES (3, 'avibryant');
> SELECT * FROM handles;
id | handle
---+--------------
0 | GabriellaG439
1 | posco
3 | avibryant
The Defaultable (Map Int)
type has a law-abiding Applicative
instance, so we can safely “join” these “tables” using Applicative
operations. For example, if we enable Haskell’s ApplicativeDo
language extension then we can compute an “inner join” on tables like this:
{-# LANGUAGE ApplicativeDo #-}
innerJoin :: Defaultable (Map Int) (String, String)
= do
innerJoin <- firstNames
firstName <- lastNames
lastName return (firstName, lastName)
… and that evaluates to the following result:
Defaultable
(fromList0, ("Gabriella","Gonzalez"))
[ (2, ("Edgar" ,"Codd" ))
, (
]
)Nothing
… which is the same result we would have gotten from doing an inner join in SQL:
> SELECT firstNames.id, firstName, lastName
> FROM firstNames INNER JOIN lastNames on firstNames.id = lastNames.id;
id | firstName | lastName
---+-----------+---------
0 | Gabriella | Gonzalez
2 | Edgar | Codd
The Defaultable (Map Int)
type also has a law-abiding Alternative
instance, which we can combine with the Applicative
instance to compute “left/right/outer joins”. For example, this “left join”:
leftJoin :: Defaultable (Map Int) (String, Maybe String)
= do
leftJoin <- firstNames
firstName <- optional lastNames
lastName return (firstName, lastName)
… evaluates to:
Defaultable
(fromList0, ("Gabriella",Just "Gonzalez"))
[ (1, ("Oscar" ,Nothing ))
, (2, ("Edgar" ,Just "Codd" ))
, (
]
)Nothing
… which is analogous to this SQL left join:
> SELECT firstNames.id, firstName, lastName
> FROM firstNames LEFT JOIN lastNames on firstNames.id = lastNames.id;
id | firstName | lastName
---+-----------+---------
0 | Gabriella | Gonzalez
1 | Oscar |
2 | Edgar | Codd
Since Haskell is a more fully-featured language than SQL, we can do more sophisticated things more easily than in SQL. For example, the following three-way join with some post-processing logic is much easier to express in Haskell than SQL:
display :: String -> Maybe String -> String -> String
Nothing handle =
display firstName <> ": @" <> handle
firstName Just lastName) handle =
display firstName (<> " " <> lastName <> ": @" <> handle
firstName
interestingJoin :: Defaultable (Map Int) String
= do
interestingJoin <- firstNames
firstName <- optional lastNames
lastName <- handles
handle return (display firstName lastName handle)
… which evaluates to:
Defaultable
(fromList0, "Gabriella Gonzalez: @GabriellaG439")
[ (1, "Oscar: @posco" )
, (
]
)Nothing
The Defaultable
type constructor
The central data type exported by the package is the Defaultable
type constructor, which has the following simple definition:
data Defaultable map value = Defaultable (map value) (Maybe value)
Here the map
type parameter can be any Map
-like type that includes the type of the key. For example, a typical instantiation of the Defaultable
type constructor might be Defaultable (Map key) value
or Defaultable IntMap value
.
The first field of the type is the actual map that you want to wrap in order to get an Applicative
and Alternative
instance. The second field is an optional default value stored alongside the map that can be returned if a lookup
does not find a matching key.
The default value is not required (it can be Nothing
), but that default value is what makes the Applicative
instance work. Specifically, without the ability to specify a default value there would be no way to implement pure
for a Map
-like type.
In case you’re curious, here is what the Applicative
instance looks like:
instance (Apply map, forall a . Monoid (map a)) => Applicative (Defaultable map)
where
pure v = Defaultable mempty (pure v)
Defaultable fMap fDefault <*> Defaultable xMap xDefault =
Defaultable fxMap fxDefault
where
= (fMap <.> xMap) <> fFallback <> xFallback
fxMap where
=
fFallback case fDefault of
Nothing -> mempty
Just f -> fmap f xMap
=
xFallback case xDefault of
Nothing -> mempty
Just x -> fmap ($ x) fMap
= fDefault <*> xDefault fxDefault
The neat part of the above instance is the class constraint:
↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓instance (Apply map, forall a . Monoid (map a)) => Applicative (Defaultable map)
The Defaultable
type is set up in such a way that you can wrap any Map
-like type that satisfies that constraint (which is basically all of them) and get a law-abiding Applicative
instance (See the Appendix for a proof of the Applicative
laws).
In particular, this constraint makes use of the QuantifiedConstraints
language extension introduced in GHC 8.6. Without that instance then we wouldn’t be able to generalize this type to wrap arbitrary Map
s and we’d have to hard-code the package to work with a specific Map
like Data.Map.Map
.
The Defaultable
type also implements Alternative
, too, although that instance is much simpler:
instance (Apply map, forall a . Monoid (map a)) => Alternative (Defaultable map) where
= Defaultable mempty empty
empty
Defaultable lMap lDefault <|> Defaultable rMap rDefault =
Defaultable (lMap <> rMap) (lDefault <|> rDefault)
This instance is only possible because the Defaultable
type constructor doesn’t require the default value to be present. If the default value were required then we could not sensibly define empty
.
Prior art
I was surprised that something like this didn’t already exist on Hackage. The closest package I could find was this one:
However, that wasn’t exactly what I wanted, because it requires the default value to be present. That means that you can’t implement an Alternative
instance for the TMap
type from that package and you therefore can’t do things like left/right/outer joins as I mentioned above.
Also, more generally, sometimes you want a Map
to have an Applicative
instance without having to specify a default value. Requiring the default to always be present is not necessary to implement Applicative
.
The other issue I had with that package is that it’s hard-coded to use Data.Map.Map
under the hood, whereas I wanted an API that could be used in conjunction with any Map
-like type.
Conclusion
The idea for this package originated from a LambdaConf presentation I gave a while ago where I brainstormed what a good “data science” ecosystem for Haskell might look like:
I sat on this idea for years without publishing anything to Hackage because my original vision was a bit too ambitious and included much more than just an Applicative
Map
type. However, recently I needed this Applicative
Map
type, so I settled for publishing a narrower and more focused package to Hackage.
The personal use case I have in mind for this package is no longer data science, but I hope that people interested in building a data science ecosystem for Haskell consider using this package as a building block since I believe it is well-suited for that purpose.
Appendix - Proof of the Applicative
laws
These proofs require a few additional assumptions about the interaction between the Apply
and Monoid
constraint on the map
type parameter to Defaultable
. These assumptions hold for Map
-like types.
The first assumption is that fmap
is a Monoid
homomorphism:
fmap f mempty = mempty
fmap f (x <> y) = fmap f x <> fmap f y
The second assumption is that f <.>
is a Monoid
homorphism:
<.> mempty = mempty
f
<.> (x <> y) = (f <.> x) <> (f <.> y) f
The final assumption is specific to maps, which is:
-- Given:
mf :: map (a -> b)
mx :: map a
kf :: (a -> b) -> c
kx :: a -> c
<.> mx) <> fmap kf mf <> fmap kx mx
(mf = (mf <.> mx) <> fmap kx mx <> fmap kf mf
The intuition here is if that map
is a Map
-like type constructor then we can think of those three expressions as having a set of keys associated with them, such that:
-- Given:
keys :: map a -> Set key
<.> mx) = keys (fmap kf mf) `intersection` keys (fmap kx mx) keys (mf
So normally the following equality would not be true:
fmap kf mf <> fmap kx mx
= fmap kx mx <> fmap kf mf
… because the result would change if there was a key collision. Then the order in which we union (<>
) the two maps would change the result.
However, if you union yet another map (mf <.> mx
) that shadows the colliding keys then result remains the same.
The proof below uses that assumption a bit less formally by just noting that we can commute a union operation if there is a downstream union operation that would shadow any colliding keys that might differ.
Proof of identity law
pure id <*> v
-- pure v = Defaultable mempty (pure v)
= Defaultable mempty (pure id) <*> v
-- Expand: v = Defaultable xMap xDefault
= Defaultable mempty (pure id) <*> Defaultable xMap xDefault
-- Definition of (<*>)
= Defaultable fxMap fxDefault
where
= (mempty <.> xMap) <> fFallback <> xFallback
fxMap where
=
fFallback case pure id of
Nothing -> mempty
Just f -> fmap f xMap
=
xFallback case xDefault of
Nothing -> mempty
Just x -> fmap ($ x) mempty
= pure id <*> xDefault
fxDefault
-- mempty <.> xMap = mempty
= Defaultable fxMap fxDefault
where
= mempty <> fFallback <> xFallback
fxMap where
=
fFallback case pure id of
Nothing -> mempty
Just f -> fmap f xMap
=
xFallback case xDefault of
Nothing -> mempty
Just x -> fmap ($ x) mempty
= pure id <*> xDefault
fxDefault
-- Simplify `case pure id of …`
= Defaultable fxMap fxDefault
where
= mempty <> fFallback <> xFallback
fxMap where
= fmap id xMap
fFallback
=
xFallback case xDefault of
Nothing -> mempty
Just x -> fmap ($ x) mempty
= pure id <*> xDefault
fxDefault
-- fmap id x = x
= Defaultable fxMap fxDefault
where
= mempty <> fFallback <> xFallback
fxMap where
= xMap
fFallback
=
xFallback case xDefault of
Nothing -> mempty
Just x -> fmap ($ x) mempty
= pure id <*> xDefault
fxDefault
-- fmap f mempty = mempty
= Defaultable fxMap fxDefault
where
= mempty < >fFallback <> xFallback
fxMap where
= xMap
fFallback
=
xFallback case xDefault of
Nothing -> mempty
Just x -> mempty
= pure id <*> xDefault
fxDefault
-- pure id <*> v = v
= Defaultable fxMap fxDefault
where
= mempty <> fFallback <> xFallback
fxMap where
= xMap
fFallback
=
xFallback case xDefault of
Nothing -> mempty
Just x -> mempty
= xDefault
fxDefault
-- Simplify
= Defaultable fxMap fxDefault
where
= mempty <> xMap <> mempty
fxMap
= xDefault
fxDefault
-- x <> mempty = x
-- mempty <> x = x
= Defaultable fxMap fxDefault
where
= xMap
fxMap
= xDefault
fxDefault
-- Simplify
= Defaultable xMap xDefault
-- Contract: v = Defaultable xMap xDefault
= v
Proof of the composition law
pure (.) <*> u <*> v <*> w
-- Expand:
-- u = Defaultable uMap uDefault
-- v = Defaultable vMap vDefault
-- w = Defaultable wMap wDefault
= pure (.)
<*> Defaultable uMap uDefault
<*> Defaultable vMap vDefault
<*> Defaultable wMap wDefault
-- pure v = Defaultable mempty (pure v)
= Defaultable mempty (pure (.))
<*> Defaultable uMap uDefault
<*> Defaultable vMap vDefault
<*> Defaultable wMap wDefault
… before continuing, it’s easiest to prove all eight possible combinations of:
uDefault
ispure u
orempty
vDefault
ispure v
orempty
wDefault
ispure w
orempty
To avoid lots of repetition, I’ll only prove the most difficult case (where all defaults are present), since the other proofs are essentially subsets of that proof where some subterms disappear because they become mempty
.
Case:
uDefault = pure u
vDefault = pure v
wDefault = pure w
= Defaultable mempty (pure (.))
<*> Defaultable uMap (pure u)
<*> Defaultable vMap (pure v)
<*> Defaultable wMap (pure w)
-- Definition of (<*>)
= (Defaultable cuMap cuDefault
where
= (mempty <.> uMap) <> fmap (.) uMap <> fmap ($ u) mempty
cuMap
= pure (.) <*> pure u
cuDefault
)<*> Defaultable vMap (pure v)
<*> Defaultable wMap (pure w)
-- mempty <.> x = mempty
= (Defaultable cuMap cuDefault
where
= mempty <> fmap (.) uMap <> fmap ($ u) mempty
cuMap
= pure (.) <*> pure u
cuDefault
)<*> Defaultable vMap (pure v)
<*> Defaultable wMap (pure w)
-- fmap f mempty = mempty
= (Defaultable cuMap cuDefault
where
= mempty <> fmap (.) uMap <> mempty
cuMap
= pure (.) <*> pure u
cuDefault
)<*> Defaultable vMap (pure v)
<*> Defaultable wMap (pure w)
-- pure f <*> pure x = pure (f x)
= (Defaultable cuMap cuDefault
where
= mempty <> fmap (.) uMap <> mempty
cuMap
= pure ((.) u)
cuDefault
)<*> Defaultable vMap (pure v)
<*> Defaultable wMap (pure w)
-- x <> mempty = x
-- mempty <> x = x
= (Defaultable cuMap cuDefault
where
= fmap (.) uMap
cuMap
= pure ((.) u)
cuDefault
)<*> Defaultable vMap (pure v)
<*> Defaultable wMap (pure w)
-- Simplify
= (Defaultable (fmap (.) uMap) (pure (u .)))
<*> Defaultable vMap (pure v)
<*> Defaultable wMap (pure w)
-- Definition of (<*>)
= (Defaultable cuvMap cuvDefault
where
=
cuvMap fmap (.) uMap <.> vMap)
(<> fmap (u .) vMap
<> fmap ($ v) (fmap (.) uMap)
= pure (u .) <*> pure v
cuvDefault
)<*> Defaultable wMap (pure w)
-- fmap f (fmap g x) = fmap (f . g) x
= (Defaultable cuvMap cuvDefault
where
=
cuvMap fmap (.) uMap <.> vMap)
(<> fmap (u .) vMap
<> fmap (. v) uMap
= pure (u .) <*> pure v
cuvDefault
)<*> Defaultable wMap (pure w)
-- ((.) u) = \v -> u . v
= (Defaultable cuvMap cuvDefault
where
=
cuvMap fmap (.) uMap <.> vMap)
(<> fmap (u .) vMap
<> fmap (. v) uMap
= pure (u .) <*> pure v
cuvDefault
)<*> Defaultable wMap (pure w)
-- pure f <*> pure x = pure (f x)
= (Defaultable cuvMap cuvDefault
where
=
cuvMap fmap (.) uMap <.> vMap)
(<> fmap (u .) vMap
<> fmap (. v) uMap
= pure (u . v)
cuvDefault
)<*> Defaultable wMap (pure w)
-- Definition of (<*>)
= Defaultable cuvwMap cuvwDefault
where
= (cuvMap <.> wMap) <> fmap (u . v) wMap <> fmap ($ w) cuvMap
cuvwMap where
=
cuvMap fmap (.) uMap <.> vMap)
(<> fmap (u .) vMap
<> fmap (. v) uMap
= pure (u . v) <*> pure v
cuvwDefault
-- (f <> g) <.> x = (f <.> x) <> (g <.> x)
-- fmap f (x <> y) = fmap f x <> fmap f y
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap fmap (.) uMap <.> vMap <.> wMap)
(<> (fmap (u .) vMap <.> wMap)
<> (fmap (. v) uMap <.> wMap)
<> fmap (u . v) wMap
<> fmap ($ w) (fmap (.) uMap <.> vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u . v) <*> pure w
cuvwDefault
-- pure f <*> pure x = pure (f x)
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap fmap (.) uMap <.> vMap <.> wMap)
(<> (fmap (u .) vMap <.> wMap)
<> (fmap (. v) uMap <.> wMap)
<> fmap (u . v) wMap
<> fmap ($ w) (fmap (.) uMap <.> vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u (v w))
cuvwDefault
-- fmap (.) u <.> v <.> w = u <.> (v <.> w)
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> (fmap (u .) vMap <.> wMap)
<> (fmap (. v) uMap <.> wMap)
<> fmap (u . v) wMap
<> fmap ($ w) (fmap (.) uMap <.> vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u (v w))
cuvwDefault
-- fmap f (x <.> y) = fmap (f .) x <.> y
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> fmap u (vMap <.> wMap)
<> (fmap (. v) uMap <.> wMap)
<> fmap (u . v) wMap
<> fmap ($ w) (fmap (.) uMap <.> vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u (v w))
cuvwDefault
-- x <.> fmap f y = fmap (. f) x <.> y
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> fmap u (vMap <.> wMap)
<> (uMap <.> fmap v wMap)
<> fmap (u . v) wMap
<> fmap ($ w) (fmap (.) uMap <.> vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u (v w))
cuvwDefault
-- fmap f (x <.> y) = fmap (f .) x <.> y
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> fmap u (vMap <.> wMap)
<> (uMap <.> fmap v wMap)
<> fmap (u . v) wMap
<> (fmap (($ w) .) (fmap (.) uMap) <.> vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u (v w))
cuvwDefault
-- fmap f (fmap g x) = fmap (f . g) x
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> fmap u (vMap <.> wMap)
<> (uMap <.> fmap v wMap)
<> fmap (u . v) wMap
<> (fmap ((($ w) .) . (.)) uMap <.> vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u (v w))
cuvwDefault
-- ((($ w) .) . (.))
-- = \u -> (($ w) .) ((.) u)
-- = \u -> (($ w) .) (u .)
-- = \u -> ($ w) . (u .)
-- = \u v -> ($ w) ((u .) v)
-- = \u v -> ($ w) (u . v)
-- = \u v -> (u . v) w
-- = \u v -> u (v w)
-- = \u v -> u (($ w) v)
-- = \u -> u . ($ w)
-- = (. ($ w))
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> fmap u (vMap <.> wMap)
<> (uMap <.> fmap v wMap)
<> fmap (u . v) wMap
<> (fmap (. ($ w)) uMap <.> vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u (v w))
cuvwDefault
-- x <.> (f <$> y) = (. f) <$> x <.> y
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> fmap u (vMap <.> wMap)
<> (uMap <.> fmap v wMap)
<> fmap (u . v) wMap
<> (uMap <.> fmap ($ w) vMap)
<> fmap ($ w) (fmap (u .) vMap)
<> fmap ($ w) (fmap (. v) uMap)
= pure (u (v w))
cuvwDefault
-- fmap f (fmap g x) = fmap (f . g) w
-- (f . g) = \x -> f (g x)
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> fmap u (vMap <.> wMap)
<> (uMap <.> fmap v wMap)
<> fmap (\w -> u (v w)) wMap
<> (uMap <.> fmap ($ w) vMap)
<> fmap (\v -> u (v w)) vMap
<> fmap (\u -> u (v w)) uMap
= pure (u (v w))
cuvwDefault
-- `fmap (\w -> u (v w)) wMap <> (uMap <.> fmap ($ w) vMap)` commutes because
-- the colliding keys are shadowed by `(uMap <.> (vMap <.> wMap))`
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> fmap u (vMap <.> wMap)
<> (uMap <.> fmap v wMap)
<> (uMap <.> fmap ($ w) vMap)
<> fmap (\w -> u (v w)) wMap
<> fmap (\v -> u (v w)) vMap
<> fmap (\u -> u (v w)) uMap
= pure (u (v w))
cuvwDefault
-- `fmap u (vMap <.> wMap) <> (uMap <.> fmap v wMap)` commutes because the
-- colliding keys are sahdowed by `(uMap <.> (vMap <.> wMap))`
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> (uMap <.> fmap v wMap)
<> fmap u (vMap <.> wMap)
<> (uMap <.> fmap ($ w) vMap)
<> fmap (\w -> u (v w)) wMap
<> fmap (\v -> u (v w)) vMap
<> fmap (\u -> u (v w)) uMap
= pure (u (v w))
cuvwDefault
-- `fmap u (vMap <.> wMap) <> (uMap <.> fmap ($ w) vMap)` commutes because the
-- colliding keys are sahdowed by `(uMap <.> (vMap <.> wMap))`
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> (uMap <.> fmap v wMap)
<> (uMap <.> fmap ($ w) vMap)
<> fmap u (vMap <.> wMap)
<> fmap (\w -> u (v w)) wMap
<> fmap (\v -> u (v w)) vMap
<> fmap (\u -> u (v w)) uMap
= pure (u (v w))
cuvwDefault
-- \w -> u (v w) = u . v
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> (uMap <.> (fmap v wMap)
<> (uMap <.> fmap ($ w) vMap)
<> fmap u (vMap <.> wMap)
<> fmap (u . v) wMap)
<> fmap (\v -> u (v w)) vMap
<> fmap (\u -> u (v w)) uMap
= pure (u (v w))
cuvwDefault
-- fmap f (fmap g x) = fmap (f . g) x, in reverse
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> (uMap <.> (fmap v wMap)
<> (uMap <.> fmap ($ w) vMap)
<> fmap u (vMap <.> wMap)
<> fmap u (fmap v wMap)
<> fmap (\v -> u (v w)) vMap
<> fmap (\u -> u (v w)) uMap
= pure (u (v w))
cuvwDefault
-- \v -> u (v w)
-- = \v -> u (($ w) v)
-- = u . ($ w)
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> (uMap <.> (fmap v wMap)
<> (uMap <.> fmap ($ w) vMap)
<> fmap u (vMap <.> wMap)
<> fmap u (fmap v wMap)
<> fmap (u . ($ w)) vMap
<> fmap (\u -> u (v w)) uMap
= pure (u (v w))
cuvwDefault
-- fmap f (fmap g x) = fmap (f . g) x, in reverse
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> (uMap <.> (fmap v wMap)
<> (uMap <.> fmap ($ w) vMap)
<> fmap u (vMap <.> wMap)
<> fmap u (fmap v wMap)
<> fmap u (fmap ($ w) vMap)
<> fmap (\u -> u (v w)) uMap
= pure (u (v w))
cuvwDefault
-- \f -> f x = ($ x)
= Defaultable cuvwMap cuvwDefault
where
=
cuvwMap <.> (vMap <.> wMap))
(uMap <> (uMap <.> (fmap v wMap)
<> (uMap <.> fmap ($ w) vMap)
<> fmap u (vMap <.> wMap)
<> fmap u (fmap v wMap)
<> fmap u (fmap ($ w) vMap)
<> fmap ($ (v w)) uMap
= pure (u (v w))
cuvwDefault
-- f <.> (x <> y) = (f <.> x) <> (f <.> y), in reverse
-- fmap f (x <> y) = fmap f x <> fmap f y, in reverse
= Defaultable cuvwMap cuvwDefault
where
= (uMap <.> vwMap) <> fmap u vwMap <> fmap ($ (v w)) uMap
cuvwMap where
= (vMap <.> wMap) <> fmap v wMap <> fmap ($ w) vMap
vwMap
= pure (u (v w))
cuvwDefault
-- pure f <*> pure x = pure (f x), in reverse
= Defaultable cuvwMap cuvwDefault
where
= (uMap <.> vwMap) <> fmap u vwMap <> fmap ($ (v w)) uMap
cuvwMap where
= (vMap <.> wMap) <> fmap v wMap <> fmap ($ w) vMap
vwMap
= pure u <*> pure (v w)
cuvwDefault
-- Definition of (<*>), in reverse
= ( Defaultable uMap (pure u)
<*> (Defaultable vwMap vwDefault
where
= (vMap <.> wMap) <> fmap v wMap <> fmap ($ w) vMap
vwMap
= pure (v w)
vwDefault
)
)
-- pure f <*> pure x = pure (f x), in reverse
= ( Defaultable uMap (pure u)
<*> (Defaultable vwMap vwDefault
where
= (vMap <.> wMap) <> fmap v wMap <> fmap ($ w) vMap
vwMap
= pure v <*> pure w
vwDefault
)
)
-- Definition of (<*>), in reverse
= ( Defaultable uMap (pure u)
<*> ( Defaultable vMap (pure v)
<*> Defaultable wMap (pure w)
)
)
-- Contract:
-- u = Defaultable uMap uDefault
-- v = Defaultable vMap vDefault
-- w = Defaultable wMap wDefault
= u <*> (v <*> w)
Proof of homomorphism law
pure f <*> pure x
-- pure v = Defaultable mempty (pure v)
= Defaultable mempty (pure f) <*> Defaultable mempty (pure x)
-- Definition of (<*>)
= Defaultable fxMap fxDefault
where
= (mempty <.> mempty) <> fmap f mempty <> fmap ($ x) mempty
fxMap
= pure f <*> pure x
fxDefault
-- fmap f mempty = mempty
= Defaultable fxMap fxDefault
where
= (mempty <.> mempty) <> mempty <> mempty
fxMap
= pure f <*> pure x
fxDefault
-- mempty <.> x = mempty
= Defaultable fxMap fxDefault
where
= mempty <> mempty <> mempty
fxMap
= pure f <*> pure x
fxDefault
-- mempty <> x = x
= Defaultable fxMap fxDefault
where
= mempty
fxMap
= pure f <*> pure x
fxDefault
-- pure f <*> pure x = pure (f x)
= Defaultable fxMap fxDefault
where
= mempty
fxMap
= pure (f x)
fxDefault
-- Simplify
= Defaultable mempty (pure (f x))
-- pure v = Defaultable mempty (pure v), in reverse
= pure (f x)
Proof of interchange law
<*> pure y
u
-- pure v = Defaultable mempty (pure v)
= u <*> Defaultable mempty (pure y)
-- Expand: u = Defaultable uMap uDefault
= Defaultable uMap uDefault <*> Defaultable mempty (pure y)
-- Definition of (<*>)
= Defaultable uyMap uyDefault
where
= (uMap <.> mempty) <> uFallback <> fmap ($ y) uMap
uyMap where
=
uFallback case uDefault of
Nothing -> mempty
Just u -> fmap y mempty
= uDefault <*> pure y
uyDefault
-- fmap f mempty = mempty
= Defaultable uyMap uyDefault
where
= (uMap <.> mempty) <> uFallback <> fmap ($ y) uMap
uyMap where
=
uFallback case uDefault of
Nothing -> mempty
Just u -> mempty
= uDefault <*> pure y
uyDefault
-- Simplify `case uDefault of …`
= Defaultable uyMap uyDefault
where
= (uMap <.> mempty) <> uFallback <> fmap ($ y) uMap
uyMap where
= mempty
uFallback
= uDefault <*> pure y
uyDefault
-- f <.> mempty = mempty
= Defaultable uyMap uyDefault
where
= mempty <> uFallback <> fmap ($ y) uMap
uyMap where
= mempty
uFallback
= uDefault <*> pure y
uyDefault
-- mempty <> x = x
= Defaultable uyMap uyDefault
where
= fmap ($ y) uMap
uyMap
= uDefault <*> pure y
uyDefault
-- u <*> pure y = pure ($ y) <*> u
= Defaultable uyMap uyDefault
where
= fmap ($ y) uMap
uyMap
= pure ($ y) <*> uDefault
uyDefault
-- pure f <*> x = fmap f x
= Defaultable uyMap uyDefault
where
= fmap ($ y) uMap
uyMap
= fmap ($ y) uDefault
uyDefault
-- Definition of `fmap`, in reverse
= fmap ($ y) (Defaultable uMap uDefault)
-- Contract: u = Defaultable uMap uDefault
= fmap ($ y) u
-- pure f <*> x = fmap f x, in reverse
= pure ($ y) <*> u