You got it, Peter!<br><br>There&#39;s a different generalization axis
I&#39;ve gotten a lot of mileage from, hinted at by the types of first and
second.&nbsp; Generalize function fmap to arrows beyond (-&gt;).&nbsp; I called
the generalized method &quot;result&quot;, because it says to edit in the result
of a function, just as first and second say to edit in the first and
second components of a pair.<br><br>This pattern emerged for me while working on the Eros (<a href="http://conal.net/papers/Eros/">http://conal.net/papers/Eros/</a>) project and is captured in the DeepArrow library (<a href="http://haskell.org/haskellwiki/DeepArrow">http://haskell.org/haskellwiki/DeepArrow</a>).&nbsp; The Eros paper shows applications for interactive composition of values, code, and UIs.<br>
<br>Even without the generalization to deep arrows, defining result = (.), and using first, second, and result gives a nice concrete reading, in addition to uses of fmap.&nbsp; For instance, (first.result.second) says how to walk down the type graph: take the first half of a pair, then the result part of a function, then the second part of a pair.<br>
<br>&nbsp; - Conal<br><br><div class="gmail_quote">2008/11/20 Peter Verswyvelen <span dir="ltr">&lt;<a href="mailto:bugfact@gmail.com">bugfact@gmail.com</a>&gt;</span><br><blockquote class="gmail_quote" style="border-left: 1px solid rgb(204, 204, 204); margin: 0pt 0pt 0pt 0.8ex; padding-left: 1ex;">
Thanks for &nbsp;the feedback.<div><br></div><div><div>Let&#39;s see if I get this by writing a little newbie tutorial for myself using literal Haskell syntax.&nbsp;</div><div><br></div><div>I will import Control.Arrow since fmap on a pairs transforms the second coordinate value,<br>

</div><div>and when transforming pairs, I sometimes need to transform the first coordinate value...</div><div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; import Control.Arrow</span></span><span style="font-family: &#39;courier new&#39;,monospace;"><br>

</span></div><div><br></div><div>To understand &nbsp;the (fmap.fmap.fmap) thing, I will create a simple tutorial,</div><div>mainly for myself to get a better understanding.&nbsp;</div><div><br></div><div>Suppose we have a pair:</div>

<div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; p :: (Bool, [Char])</span></span></div><div><br></div>
<div>E.g.</div><div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; p = (True,&quot;Reactive&quot;)</span></span></div>
<div><br></div><div>The &quot;type graph&quot; (pardon my lack of knowledge of correct terminology) of p is</div><div><br></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;(,)</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; / &nbsp; \</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; Bool &nbsp;[] &nbsp;&nbsp;</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Char</span></span></div>

<div><br></div><div>Since fmap is about transforming a structure into another structure,</div><div>let&#39;s suppose that - given any &quot;instance&quot; with a type signature like p -</div><div>we want to create a new instance p&#39; at runtime that transforms</div>

<div>the string (at the second coordinate) into its length.</div><div><br></div><div>That&#39;s easy; we can use fmap (or Arrow.second) to do that:</div><div><br></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&lt; instance Functor ((,) a) where</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&lt; &nbsp; fmap f (x,y) = fmap (x, f y)</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;"><br>

</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; tp :: (Bool,[Char]) -&gt; (Bool,Int)</span></span></div>
<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; tp = fmap length&nbsp;</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt;</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; p&#39; = tp p</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;"><br>

</span></span></div><div>fmap on pairs basically transforms the rightmost branch of our graph.</div><div><br></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;(,) &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (,) &nbsp; &nbsp;&nbsp;</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; / &nbsp; \ &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; / &nbsp; \ &nbsp; &nbsp;</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; Bool &nbsp;[] &nbsp; --&gt; &nbsp;Bool &nbsp;Int &nbsp;&nbsp;</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; | &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Char &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</span></span></div>

<div><br></div><div>fmap always transforms the rightmost branch in the graph, since the kind of Functor is * -&gt; *.&nbsp;</div><div><br></div><div>For example lets define an fmap on triples:</div><div><br></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; instance Functor ((,,) a b) where</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; &nbsp; &nbsp;fmap f (x,y,z) = (x,y,f z)</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;"><br>

</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&lt; fmap :: (c-&gt;d) -&gt; (a,b,c) -&gt; (a,b,d)</span></span></div>
<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;"><br></span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;(,,) &nbsp; &nbsp; &nbsp; &nbsp; (,,)</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; / | \ &nbsp; --&gt; &nbsp;/ | \</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp;a &nbsp;b &nbsp;c &nbsp; &nbsp; &nbsp;a &nbsp;b &nbsp;d</span></span></div>

<div><br></div><div>To continue the (fmap.fmap.fmap) story, suppose we now nest p in a Maybe:</div><div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; m :: Maybe (Bool, [Char])</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; m = Just (True, &quot;Reactive&quot;)</span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;"><br>

</span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; &nbsp; &nbsp; Maybe&nbsp;</span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; |</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;(,)</span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; &nbsp; &nbsp; / &nbsp; \</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; &nbsp; Bool &nbsp;[] &nbsp;&nbsp;</span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Char</span></span></div><div><br></div><div>Again we want to transform the string into its length.</div>
<div><br></div><div>To do that we can use the fmap Maybe instance:</div><div><br></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&lt; fmap f Nothing = Nothing</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&lt; fmap f (Just x) = Just (f x)</span></span></div><div><br></div><div>
The function we need to fmap on m is just tp!</div><div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; tm :: Maybe (Bool,[Char]) -&gt; Maybe (Bool,Int)</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; tm = fmap tp</span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt;</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; m&#39; = tm m</span></span></div><div><br></div><div>So again this fmap transforms the rightmost branch underneath the Maybe</div>

<div>(which is the one and only branch underneath the unary Maybe type)</div><div><br></div><div>If we expand tm we get</div><div><br></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&lt; tm = fmap (fmap length) = <span style="color: rgb(0, 153, 0);">(fmap . fmap)</span> length</span></span></div>

<div><br></div><div>So here we have the first magical (fmap . fmap):&nbsp;</div><div>- the first fmap transforms the branch underneath the Maybe with (fmap (fmap length)),</div><div>- the second fmap transforms the right branch underneath the pair (,) with (fmap length).</div>

<div><br></div><div>We can also do this for functions. Suppose we now have</div><div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; f :: Char -&gt; Maybe (Bool, [Char])</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; f c = Just (c==&#39;a&#39;, &quot;Reactive&quot;)</span></span></div>
<div><br></div><div>The type graph of f is</div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;"><br></span></span></div>
<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp;(-&gt;)</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; / &nbsp; &nbsp;\</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">Char &nbsp; Maybe&nbsp;</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; |</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp;(,)</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; / &nbsp; \</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; Bool &nbsp;[] &nbsp;&nbsp;</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; |</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;Char</span></span></div><div><br></div><div>But function application also has an fmap instance!&nbsp;</div>
<div><br></div><div>It is just the same as function composition:</div><div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&lt; instance Functor ((&lt;-) a) where</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&lt; &nbsp; fmap f g = f . g</span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&lt; fmap :: (b-&gt;c) -&gt; (a-&gt;b) -&gt; (a-&gt;c)</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;"><br></span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; (-&gt;) &nbsp; &nbsp; &nbsp; &nbsp; (-&gt;) &nbsp;&nbsp;</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp; / &nbsp;\ &nbsp; &nbsp;-&gt; &nbsp; / &nbsp;\ &nbsp;&nbsp;</span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&nbsp;&nbsp;a &nbsp; &nbsp;b &nbsp; &nbsp; &nbsp; a &nbsp; &nbsp;c</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;"><br></span></span></div><div>Again the rightmost branch is transformed...</div>
<div>&nbsp;&nbsp;</div><div>So to transform the string into its length but now in the f graph, we do</div><div><br></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; tf :: (Char -&gt; Maybe (Bool, [Char])) -&gt; (Char -&gt; Maybe (Bool, Int))</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; tf = fmap tm</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt;</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; f&#39; = tf f</span></span></div><div><br></div><div>Expanding this gives</div>
<div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; tf&#39; = (<span style="color: rgb(0, 153, 0);">fmap . fmap . fmap)</span> length</span></span></div>
<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; f&#39;&#39; = tf&#39; f</span></span></div><div><br></div><div>So the expression ((fmap.fmap.fmap) g) performs deep transformation&nbsp;</div>

<div>on the 3 rightmost branches of any type graph (that has fmap instances)</div><div><br></div><div>To transform a leftmost branch, we can use Arrow.first, for example:</div><div><br></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; tf&#39;&#39; :: (Char -&gt; Maybe (Bool,[Char])) -&gt; (Char -&gt; Maybe (String,[Char]))</span></span></div>

<div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; tf&#39;&#39; = (fmap . fmap . first) show&nbsp;</span></span></div><div>
<span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;"><br></span></span></div><div><span style="font-weight: bold;"><span style="font-family: &#39;courier new&#39;,monospace;">&gt; f&#39;&#39;&#39; = tf&#39;&#39; f</span></span></div>

<div><br></div><div>Demo:<br></div><div><br></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; main = mapM_ putStrLn&nbsp;</span></span></div>
<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; &nbsp; &nbsp; &nbsp; &nbsp;[ showT p $ fmap length</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; &nbsp; &nbsp; &nbsp; &nbsp;, showT m $ (fmap.fmap) length</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; &nbsp; &nbsp; &nbsp; &nbsp;, showF f $ (fmap.fmap.fmap) length</span></span></div><div>
<span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; &nbsp; &nbsp; &nbsp; &nbsp;, showF f $ (fmap.fmap.first) show ]</span></span></div><div><div>
<span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt;</span></span></div><div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; showT x t = show x ++ &quot; ==&gt; &quot; ++ (show $ t x)</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;">&gt; showF f t = &quot;\&#39;a&#39; -&gt; &quot;++show (f &#39;a&#39;) ++ &quot; ==&gt; \&#39;a&#39; -&gt; &quot; ++ show ((t f) &#39;a&#39;)</span></span></div>

<div><span style="font-family: &#39;courier new&#39;,monospace;"><span style="font-weight: bold;"><br></span></span></div><div><span style="font-family: &#39;courier new&#39;; font-size: 12px; font-weight: bold;"><div>
<span style="font-weight: normal;"><span style="font-style: italic;">|(True,&quot;Reactive&quot;) ==&gt; (True,8)</span></span></div><div><span style="font-weight: normal;"><span style="font-style: italic;">|&nbsp;Just (True,&quot;Reactive&quot;) ==&gt; Just (True,8)</span></span></div>

<div><span style="font-weight: normal;"><span style="font-style: italic;">|&nbsp;&#39;a&#39; -&gt; Just (True,&quot;Reactive&quot;) ==&gt; &#39;a&#39; -&gt; Just (True,8)</span></span></div>
<div><span style="font-weight: normal;"><span style="font-style: italic;">|&nbsp;&#39;a&#39; -&gt; Just (True,&quot;Reactive&quot;) ==&gt; &#39;a&#39; -&gt; Just (&quot;True&quot;,&quot;Reactive&quot;)</span></span></div>
<div><br></div></span></div></div><div>I think I learned something cool here: being able to perform deep transformations without writing a lot of boiler plate code.&nbsp;</div><div><br></div><div>Thank you!</div><div><br></div>

</div>
<br>_______________________________________________<br>
Reactive mailing list<br>
<a href="mailto:Reactive@haskell.org">Reactive@haskell.org</a><br>
<a href="http://www.haskell.org/mailman/listinfo/reactive" target="_blank">http://www.haskell.org/mailman/listinfo/reactive</a><br>
<br></blockquote></div><br>