In Functional reactive partner dancing, I mentioned that (a) the partially applied leading and following types have boilerplate
Applicative instances, and (b) the leading type corresponds to varying (reactive) values. Today I realized that those boilerplate instances are not very useful, and that they do not correspond to the
Applicative instance of
Reactive. In this post, I give a useful
Applicative instance that does correspond to the
Reactive instance. The instance definition is expressed in terms of the pair editor bot shown at the end of the “dancing” post, which seems to have a variety of applications.
Applicative instance has one awkward aspect that suggests a tweak to the formulation of leading. I give simplified versions of pair editing and
Applicative for the revised type. This change is in version 0.1 of the Bot libary.
Edit 2008-02-15: added FRP tags; prose tweak.
The multi-output versions of leading and following are formulated simply as the single-output versions, with a list-valued output type:
newtype a :-> b = Follows (Follow a [b]) newtype a :>- b = Leads (Lead a [b])
Partially applied, each of these types is a sort of composition of type constructors. For instance,
(:->) a is the type composition of
Follow a and
. Since both of those type constructors are applicative functors, there are standard definitions of
instance Functor ((:->) i) where fmap f (Follows z) = Follows ((fmap.fmap) f z) instance Applicative ((:->) i) where pure x = Follows ((pure.pure) x) Follows f <*> Follows x = Follows (liftA2 (<*>) f x)
and similarly for
In fact, these instance templates are abstracted into instances for the type composition operator
(:.) found in TypeCompose, so we can get the four instances for free if we define
type (:->) a = Follow a :.  type (:>-) a = Lead a :. 
Functor instances work fine, the rub (which I didn’t realize when writing the “dancing” post) is that the
Applicative instances are not what I want. They delegate to the
Applicative instances for
Follow a and for
. The result is that each output of
lf <*> lx is the list of
f x for all
f in the (list-valued)
lf output at that point and all
x in the (list-valued)
lx output. In particular, the
lf <*> lx will have an output only when both
lx have simultaneous outputs.
Instead, I’d like
lf <*> lx to have an output whenever either
lx has an output. If
lf has an output
f, I want to output
f x, where
x is the most recent
lx output. Similarly, if
lx has an output, I want to output
f x, where
f is the most recent
lf output. This behavior is exactly how
Applicative works for
Reactive, as described in reactive-normal-form.
A solution and a problem
At the end of Functional reactive partner dancing, I showed a tool that is related to the desired
editPairF :: (c,d) -> Either c d :-> (c,d)
Given an initial pair,
editPairF accepts replacements of either the first or second element and produces updated pairs, remembering the previous values. Since memory is involved,
editPairL is defined in terms of the generic accumulation combinator
editPairF to work, to pair up two follows into a pair-valued follow. A new pair is output whenever either element gets output.
pairF :: (b,c) -> a:->b -> a:->c -> a:->(b,c) pairF bc ab ac = (Left <$> ab) `mappend` (Right <$> ac) >>> editPairF bc
We had to supply the initial pair here, because follows don’t have initial values. Leads do, however, so the lead-pairing function has a simpler-looking type:
pairL :: a:>-b -> a:>-c -> a:>-(b,c)
The definition of
pairL works in terms of
pairF. The extra work involves disassembling and reassembling leads into and from initial values and follows.
ab `pairL` ac = leads (liftA2 (,) bs cs) $ pairF (b,c) abf acf where (bs,abf) = splitL ab (cs,acf) = splitL ac -- Oh dear. b & c might not be well-defined b = last bs c = last cs
The awkward bit here is that, as formulated, a multi-lead (
a :>- b) could have multiple values even initially. For that reason, I (a) use a cross product (
liftA2 (,)) for the initial pairs, and (b) extract a single value from each lead to use in the pair-valued lead’s initial value. This second consideration is worse than awkward; it will fail if either initial value list is empty.
Is it really useful for a lead to have an initial list of values? Not that I know of. I allowed the flexibility because it made the type definitions so simple and uniform, which I’ve found has a decisive impact on the simplicity of code that works on the type.
Placing the initial-list problem aside for now, here is the simple and useful Applicative instance for leads. The
pure method makes a lead with an initial value an no future reponses. The
(<*>) method uses
pairL above to make a lead whose outputs are function/argument pairs, and then maps uncurried function application onto the pairs to get out the results.
instance Applicative ((:>-) i) where pure x = leads [x] mempty lf <*> lx = uncurry ($) <$> (lf `pairL` lx)
(:->) (following)? I don’t think an
Applicative instance like the leading one can exist, due to lack of initial values. Perhaps there is an
Applicative instance corresponding to the one on Event (combining all pairs of occurrences over time).
A tweak, and we’re back to safe & elegant
The difficulty with
pairL comes from a feature of dubious value, namely having an initial list of values rather than exactly one initial value. Let’s consider removing that flexibility. Replace
newtype a :>- b = Leads (Lead a [b])
newtype a :>- b = Leads (b, a :-> b)
The pairing combinator for leads is now simpler. It’s also bomb-proof, since it has initial single values instead of lists:
pairL :: a:>-b -> a:>-c -> a:>-(b,c) Leads (a,fa) `pairL` Leads (b,fb) = Leads ((a,b), pairF (a,b) fa fb)
And we have simple and (I think) trouble-free instances:
instance Functor ((:>-) i) where fmap f (Leads (b,fol)) = Leads (f b, fmap f fol) instance Applicative ((:>-) i) where pure x = Leads (x,mempty) lf <*> lx = uncurry ($) <$> (lf `pairL` lx)