
On Thu Mar 23 15:03:36 UTC 2017, Edward Kmett wrote:
On Thu, Mar 23, 2017 at 6:18 AM, Anthony Clayden wrote:
Why is `[a]` hard-coded in the signature for concat? I would expect anything specific to Lists to be genericised: concat :: (Foldable t1, Foldable t2) => t1 (t2 a) -> t2 a
Foldable can't be used to 'construct' in this way.
Thanks Edward for the comprehensive reply. Yes I know Foldable isn't powerful enough to construct. I wasn't seriously proposing that as the signature. I was pointing out that there were several value judgments in arriving at the FTP changes. The decision has gone different ways in different places. It's not all as predictable by cold logic as you're trying to make out below. And you've left people surprised. FTP left `concat` hard-coded to Lists. It left `map` hard-coded to Lists. Why didn't it leave `length` hard-coded to Lists? The promise with FTP was to not break code, and for code that already compiled to continue to work with the same behaviour. Mostly that's been delivered. The counterpart hasn't been delivered: inappropriate uses of some methods (which therefore didn't previously compile -- good) do now compile, and whatever they calculate is a surprise/non-intuitive for a significant number. To my intuition, "length" -- as an English word -- is about structures that have length. I can see the benefit you point out below for having a generic method for size of a Foldable. Most of those Foldables I don't see as having "length".
Neither Foldable f nor even Traversable f give you the power to construct a new 'f' with different shape.
Exactly. So why even bother with concat (Right [...]); or concat (Just [...]) ?
concat is just a type restricted version of fold = foldMap id provided basically for legacy compatibility.
OK. It's arbitrary but your justification is it was always arbitrary. FTP `concat` does the same as pre-FTP `concat`. "For legacy compatibility". By that same argument: pre-FTP `length` wasn't even in Foldable. It was arbitrarily restricted to Lists. "For legacy compatibility" it should have stayed restricted to Lists.
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.
`length` was never in Data.Foldable. There was never a more general version. You're blowing smoke.
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.
Why have an instance of Foldable which always returns length 1? (Or for some instances returns length 0 or 1.)
Say you go to write a function
toVector :: Foldable f => f a -> Vector a toVector xs = Vector.fromListN (length xs) (toList xs)
With length in Foldable, ...
No. Length was not in Foldable. You should have stopped to ask why! You give good reasons for having a method in Foldable that consistently gives a size. Those are not good reasons for calling that method `length` or for breaking expectations that `length` applies for Lists, Not for Foldables.
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'?
Yes! Or `cardinality`.
That is another color the bikeshed could have been
colored,
That's an arrogant comment. You've allowed mopeds, motorbikes and skateboards into the bikeshed. And now there's not enough room for the bikes. Now you blow smoke that it's only about the colour of the shed.
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.
Data.Set has `size`. It used to have `cardinality`, that was removed. OK not the Prelude, but close enough. `size` in Data.Set does exactly what `length` now does.
`concat` and `length` should not be in Foldable. They should be in a separate class `Consable` (say), which would be a sub-class of Foldable.
length being in such a class would mean that every author
.. Would use a sensibly-named method (`size`) when they wanted the, er, size of a Foldable. And using `length` would document the code that they were applying to something that might be lengthy.
... This would have also meant dumping a brand new abstraction in Prelude without the preceding decade worth of experimentation on what works well.
As it is, we've got what? A brand new abstraction (called `length`) in Foldable without a preceding decade worth of experimentation on whether length makes sense for Either or Maybe. Furthermore without a preceding decade worth of telling people that length [1, 2] is 2; but length (1, 2) is 1.
In general the FTP didn't seek to go out of its way to make up new classes, merely to standardize existing code ...
You're blowing smoke again. There was no existing code using `length` of a Foldable, because `length` wasn't a method.
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.
OK. How well communicated was that? It certainly couldn't have applied for `length`. The semantics of existing Haskell programs was that `length` worked for Lists. Only for Lists. Not Foldables in general.
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,
Well those folks were wrong. What they should have noticed was that `size`/null fit.
...
With the choices made all of the "mainstream" base modules now have no name conflicts and can be imported unqualified.
Good. I am not criticising the FTP change in general, nor its objectives. But moving `length` from Prelude to Foldable isn't justified on those grounds. There wasn't previously a `size` (or equivalent) in Foldable. Why introduce it when there was no experience of "using for a decade" -- if that was the criterion?
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.
We were very dubious about the response we'd get introducing new, untested, names to Prelude ...
So you preferred to introduce new, unattested behaviour. `length` of an Either is unattested. `length` of a Maybe is unattested. That `length` of (1, 2) is 1 is unattested.
as everyone's code is impacted by that choice
and folks happen to be using all the "good" names for such
Again, no: nobody's code could have been using Foldable length. things, Yes that's a problem.
and nobody had done so in the preceding 17 years,
Because they couldn't. Because `length` applied only for lists.
so any changes along those lines are surprising.
So do you think it unsurprising asking for the length of a Maybe? Do you think it unsurprising that the length of (1, 2) is 1? Note: "length", not "size". So did it come as a surprise that people are surprised? I'm not surprised newbies are surprised. They're using tutorials saying length applies for Lists. And concat applies for Lists of Lists. It seems to be not just newbies surprised, judging by how often the question comes up. AntC