On Mon, Feb 7, 2011 at 11:30 PM, Ivan Lazar Miljenovic <span dir="ltr">&lt;<a href="mailto:ivan.miljenovic@gmail.com">ivan.miljenovic@gmail.com</a>&gt;</span> wrote:<br><div class="gmail_quote"><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<div class="im">On 8 February 2011 09:57, John Lato &lt;<a href="mailto:jwlato@gmail.com">jwlato@gmail.com</a>&gt; wrote:<br>
&gt; I think the real problem we have with container classes has a lot more to do<br>
&gt; with what we would use them for.  That is, Haskell already has Monoid,<br>
&gt; Foldable and Traversable.  These three (especially Foldable) cover nearly<br>
&gt; everything OOP programmers would expect out of generic container operations.<br>
<br>
</div>That was what my rewrite was going to be using.  The problem, however,<br>
is two-fold:<br>
<br>
* Dealing with types of kind * vs kind * -&gt; *</blockquote><blockquote class="gmail_quote" style="margin:0 0 0 .8ex;border-left:1px #ccc solid;padding-left:1ex;">
<br>
* Dealing with types of kind * -&gt; * that have a restriction on the<br>
type parameter (e.g. Set).<br>
<br>
I was basing my approach on Ganesh&#39;s rmonad [1] library whilst taking<br>
into account the Functor =&gt; Applicative =&gt; Monad hierarchy when<br>
re-defining the classes, but the approach was very quickly becoming<br>
unwieldy.<br></blockquote><div><br></div><div> I think the Functor =&gt; Applicative =&gt; Monad hierarchy should be orthogonal to container design.  Although many containers form useful monads, those monads can have very different behavior (e.g. List and Set).  Therefore very few (if any) algorithms can both be usefully polymorphic with respect to the container instance and also take advantage of the monadic structure.  For those algorithms this is useful, adding an extra Monad to the context is entirely appropriate.</div>
<div><br></div><div>Probably more importantly, what users want from a container is often not what the monad provides.  Although the List monad is very elegant, I usually find the semantics of ZipList more useful.  This leads me to believe that getting &quot;singleton&quot; and &quot;&lt;*&gt;&quot; from a class other than Applicative is the correct approach.</div>
<div><br></div><div>Functor is generally better-suited to containers, except for types of kind * that aren&#39;t functors.  I would suggest there&#39;s a better approach than the rmonad machinery, perhaps something like this:</div>
<div><br></div><div>&gt; class Container c where</div><div>&gt;     type Elem c :: *</div><div>&gt;</div><div>&gt; class (Container cIn, Container cOut) =&gt; CMap cIn cOut where</div><div>&gt;     cmap :: (Elem cIn -&gt; Elem cOut) -&gt; cIn -&gt; cOut</div>
<div>&gt;</div><div>&gt; instance (a ~ Elem (c a), b ~ Elem (c b), Functor c, Container (c a), Container (c b)) =&gt; CMap (c a) (c b) where</div><div>&gt;     cmap = fmap</div><div><br></div><div>Now we have the same operation which fmap provides, only we can define it for any container type.  Better, we have an instance which is suitable for all Functor instances, so we need only write instances for containers of kind * (e.g. ByteString).</div>
<div><br></div><div>An annoyance with this approach is that the output type of cmap (c b) can&#39;t be determined from the input function, so it&#39;s sometimes necessary to add type annotations.  I think this can be solved by using bidirectional fundeps instead of associated types though.</div>
<div><br></div><div>If you design container classes so that the element type is part of the class instance, restrictions aren&#39;t a problem.  It&#39;s only when you try to accommodate Functor etc. that workarounds such as rmonad become necessary.</div>
<div><br></div><div>John</div></div>