<div dir="ltr"><div class="gmail_extra"><div class="gmail_quote">On Thu, Mar 23, 2017 at 6:18 AM, Anthony Clayden <span dir="ltr"><<a href="mailto:anthony_clayden@clear.net.nz" target="_blank">anthony_clayden@clear.net.nz</a>></span> wrote:<br><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex"> Why is `[a]` hard-coded in the signature for concat?<br>
I would expect anything specific to Lists to be<br>
genericised:<br>
concat :: (Foldable t1, Foldable t2) => t1 (t2 a) -> t2<br>
a<br></blockquote><div> </div><div>Foldable can't be used to 'construct' in this way. There is no way to write a function with the type you gave. Neither Foldable f nor even Traversable f give you the power to construct a new 'f' with different shape.</div><div><br></div><div><div><span style="font-family:monospace,monospace">Prelude Data.Foldable> :t concat</span><br style="font-family:monospace,monospace"><span style="font-family:monospace,monospace">concat :: Foldable t => t [a] -> [a]</span><br style="font-family:monospace,monospace"><span style="font-family:monospace,monospace">Prelude Data.Foldable> :t fold</span><br style="font-family:monospace,monospace"><span style="font-family:monospace,monospace">fold :: (Monoid m, Foldable t) => t m -> m</span><br style="font-family:monospace,monospace"></div><div><br></div><div>concat is just a type restricted version of fold = foldMap id provided basically for legacy compatibility.</div><div><br></div></div><div>concat/concatMap can be seen as restricted versions of fold/foldMap, which are generalized in a variant of the manner that you seek, except for the small consideration that they can actually exist. =)</div><div><br></div><div><div>The Foldable/Traversable proposal was to bring the functions that already existed in base in Data.Foldable into the Prelude and eliminate the monomorphic versions of those combinators that made importing these more general versions painful for users. We didn't set out to make new functions, merely standardize on the ones folks had been using for a decade. concat was left intact, because there was already a generalization available, and no particular reason to break all the existing code that expected it.</div></div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Why have an instance of Foldable which always returns<br>
length 1?<br>
(Or for some instances returns length 0 or 1.)<br></blockquote><div><br></div><div>Say you go to write a function</div><div><br></div><div><font face="monospace, monospace">toVector :: Foldable f => f a -> Vector a</font></div><div><font face="monospace, monospace">toVector xs = Vector.fromListN (length xs) (toList xs)</font></div><div><br></div><div>With length in Foldable, you can use and for many container types it'll compute the length in O(1) rather than a separate pass.</div><div><br></div><div>Without length as a member of Foldable then we must <i>always</i> do two passes through the container.</div><div><br><div><font face="monospace, monospace">toVector :: Foldable f => f a -> Vector a</font></div><div><font face="monospace, monospace">toVector xs = Vector.fromListN (getSum $ foldMap (\_ -> Sum 1) xs) (toList xs)</font></div><div><font face="monospace, monospace"><br></font></div>or rely on Vector internally repeatedly doubling the guessed at size of the vector and trimming at the end.</div><div><br></div><div>A number of container types can use 'length' rather than having to supply their own monomorphic size function. Would 'length' have been better called 'size'? That is another color the bikeshed could have been colored, but it would have involved taking a fresh name, possibly in the Prelude, and the name collisions of a common name with existing code was considered to be the greater evil.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
`concat` and `length` should not be in Foldable.<br>
They should be in a separate class `Consable` (say),<br>
which would be a sub-class of Foldable.<br></blockquote><div><br></div><div>length being in such a class would mean that every author of such a toVector combinator above would now be hoist on the horns of the dilemma: "Do I write this so it works in the most situations and accept two passes, or do I write it with more constraints." This logic leads to multiple implementations of every function having to be in scope with simple variations in names. This would have also meant dumping a brand new abstraction in Prelude without the preceding decade worth of experimentation on what works well. In general the FTP didn't seek to go out of its way to make up new classes, merely to standardize existing code that we knew people knew how to handle. As I'll note in the following bit, that didn't _quite_ work out perfectly, but there was initially no intention of doing "new" API design, just to do the obvious things.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
Note that `length` used not to be in Foldable.<br>
`concat` was, but restricted to Lists, as now.<br></blockquote><div><br></div><div>While working on the Foldable/Traversable Proposal what happened was we found we needed to add a number of methods to the Foldable class to avoid silent changes in the semantics of existing Haskell programs. Some Foldable combinators folded in different directions than their monomorphic counterparts. </div><div><br></div><div>Along the way as we worked to fix these infelicities and noticed that as a side-effect that this allowed Foldable authors some power to execute these operations with better asymptotics where possible for their containers. This nice knock-on effect lets folks shed some names that they were taking from the environment by using what Prelude offers. During this process some folks noticed that length/null fit into the scheme, and separately proposed adding them. If they aren't in the class you get the above dilemma for users of accepting the more general Foldable type with less performance or picking up a second constraint, and they are trivially implementable as folds, so the fit within the purview of the class, so we chose to incorporate the proposal in question. We then chose to export them as the length/null from the Prelude on the same grounds as the rest of the Foldable/Traversable proposal, so that users could avoid having to import Data.Foldable explicitly and that names we take wouldn't conflict with Prelude. <br><br>With the choices made all of the "mainstream" base modules now have no name conflicts and can be imported unqualified.</div><div><br></div><blockquote class="gmail_quote" style="margin:0px 0px 0px 0.8ex;border-left:1px solid rgb(204,204,204);padding-left:1ex">
If there's a need for a method in Foldable<br>
to return 1 for a tuple, or 0..1 for Maybe/Either,<br>
don't call it `length`, call it `cardinality` (say).<br>
(This is the same approach as for separate `map`, `fmap`,<br>
`foldMap`.)</blockquote><div> </div>Could we have named them flength (or size) and fnull so that people had to ask for them explicitly and left length/null alone? Yes. But it would have meant taking new names from the Prelude or not re-exporting them from Data.Foldable, and the AMP/FTP tried to very careful about introducing any new names to the Prelude, and wanted users to be able to supply instances without having to explicitly import Data.Foldable. <br><br>We were very dubious about the response we'd get introducing new, untested, names to Prelude as everyone's code is impacted by that choice and folks happen to be using all the "good" names for such things, and nobody had done so in the preceding 17 years, so any changes along those lines are surprising.<br><div><br></div><div>-Edward</div></div></div></div>