Deprecate Foldable for Either

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
76 messages Options
1234
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Edward Kmett-2
Sadly, hlint remains fairly (er.. completely?) ignorant of the types of an expression. It has no idea what is in scope, and just provides syntax-directed guidance, so hlint isn't the answer here.

That said, there does seem to be a plausible solution available.

I have zero objection to adding {-# POISON #-} pragma that users could include in their own code that just makes it so that whatever instance is named inside of it becomes unusable. You could view it as setting up an ambiguous instance declaration that is currently impossible to write, so any attempt to use the instance would complain about ambiguous instances (or preferably about the instance being poisoned and name the source of the poison).

This would make the status quo the norm, but then you could modify your custom Prelude or what have you to {-# POISON instance Foldable ((,) e) #-} and then any code that transitively depended on your Prelude would complain if it attempted to use that instance.

Such a poison pragma should be sound with respect to GHC's internals. It is effectively making an extra instance just to cause conflicts later on in the instance selection process. This is the same as if we had Foldable the class being defined in one module, the real instance in another, someone's poison instance in a third, then a fourth module that imports the whole diamond. Any attempt to use the instance that is defined in two contradictory ways in that 4th module today will complain.

Folks who care about these instances would then have the choice about whether to avoid any package that poisoned any instances they cared about, and folks who fee strongly about this issue would be able to live in a world where these instances didn't affect any code they wrote and if they didn't want to turn off users that care about being poisoned, could turn on the poisoning through cabal flags so that they don't infect their API but get an opt-in internal consistency check for local compilation.

-Edward Kmett

P.S. The term poison above derives from the C convention of poisoning names you don't want to see in your code due to safety considerations. e.g. In GCC you can do so with

#pragma GCC poison printf sprintf fprintf


On Sat, Mar 18, 2017 at 8:51 PM, <[hidden email]> wrote:


> El 18 mar 2017, a las 16:01, Lana Black <[hidden email]> escribió:
>
>> On 18/03/17 19:49, Henning Thielemann wrote:
>>
>>> On Sat, 18 Mar 2017, Carter Schonwald wrote:
>>>
>>> for what?
>>
>> A warning if someone e.g. calls 'length (a,b)', or more generally, if
>> certain instances are used.
>> _______________________________________________
>> Libraries mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
> Please no. Many of us like our code Wall-clean while still being able to
> write polymorphic functions. Adding more warnings that are often
> triggered by correct code (redundant constraints, anyone?) only leads to
> more headache.
>
> You could make that an hlint rule on the other hand.

Can it be a hlint rule? It seems quite difficult to predict that "length" will not ever be passed e.g. a 2-tuple in the general case, within hlint.

I would also favor a warning, and happily have -Wall not include it (though I'd prefer inclusion).

Tom


> _______________________________________________
> Libraries mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Tony Morris-4
In reply to this post by amindfv

The criteria for determining this apparent danger, in all cases I have seen, is arbitrary nonsense. Try defining it in a coherent way.

"But the inconsistency with my intuition for the function named l-e-n-g-t-h that I learned in C programming school in the 1980s" is the best criteria that I have seen, and which commands outright dismissal. I am sceptical of anything better, and therefore, worth considering.

Other contenders:
* feels bad in my bones
* don't like it
* there's lots of us!

All these arguments have been, by moral imperative, and in practice, dismissed.

The length of ((,) a) is very definitely 1. That this is not bleedingly obvious, busted intuitions aside, is becoming more laborious as this not-really-debate drags on.

We have a kind system, we have a type system, we have parametricity. Use them. If you choose not to, fine, but don't try to take superior tools away from me for no benefit. I will stop you.

----

An anecdote on danger. A twin piston engine aircraft recently crashed into a shopping centre on take-off from an airport in southern Australia. All onboard were killed. Nobody on the ground was injured. The cause is yet unknown and is under investigation. It is an unusual circumstance. A friend of mine declared to me that the airport should be closed. When I asked why, I was told, "I used to work in the same type of shopping centre, at a different airport, and I felt like I was in danger when aeroplanes flew overhead." It is extremely fortunate that we don't have such ridiculous assessments of danger dictating the policies of aviation safety. Indeed, flying aeroplanes is one very effective way to get away from that nonsense. Imagine if that "sense of danger" were to dictate aviation safety regulations. What a dangerous world we would be living in!

----

Ask yourself where the danger is and be reasonable about it. Hope that helps.

On 20/03/17 10:30, [hidden email] wrote:


El 19 mar 2017, a las 17:19, Tony Morris <[hidden email]> escribió:

That you can't should be a hint that length for ((,) a) is very definitely 1.


To be frank, this seems like a non-sequitur. Many of us find e.g.:

length (_, _) = 1
maximum (_, b) = b

to be dangerous instances. We don't want to accidentally call them. We'd like a way to opt out or warn on use.

The inability to opt out or be warned does not seem to me to be an argument that the above instances are good instances.

Feel free to clarify if I haven't understood your point.

Thanks!
Tom


Simply, use a different function, not length, which is well-defined for ((,) a) and other instances.

On Mon, Mar 20, 2017 at 3:15 AM, <[hidden email]> wrote:
Is there a clear way to implement this instance warning? I.e. given:

f x = 2 * length x

Can we guarantee at compile time that "f" will never be passed a 2-tuple?

Tom

El 18 mar 2017, a las 20:04, Adam Bergmark <[hidden email]> escribió:

I'm on the fence about the instance existing. I'm +1 for a warning, and thus would be +1 on keeping the instance. +1 on making the warning opt-in and +1 keeping it out of -Wall.



On Sun, 19 Mar 2017 at 00:51 <[hidden email]> wrote:


> El 18 mar 2017, a las 16:01, Lana Black <[hidden email]> escribió:
>
>> On 18/03/17 19:49, Henning Thielemann wrote:
>>
>>> On Sat, 18 Mar 2017, Carter Schonwald wrote:
>>>
>>> for what?
>>
>> A warning if someone e.g. calls 'length (a,b)', or more generally, if
>> certain instances are used.
>> _______________________________________________
>> Libraries mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
> Please no. Many of us like our code Wall-clean while still being able to
> write polymorphic functions. Adding more warnings that are often
> triggered by correct code (redundant constraints, anyone?) only leads to
> more headache.
>
> You could make that an hlint rule on the other hand.

Can it be a hlint rule? It seems quite difficult to predict that "length" will not ever be passed e.g. a 2-tuple in the general case, within hlint.

I would also favor a warning, and happily have -Wall not include it (though I'd prefer inclusion).

Tom


> _______________________________________________
> Libraries mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries




_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

signature.asc (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

amindfv
In reply to this post by Edward Kmett-2
Edward: this is clever! I can't at the moment see any reason this wouldn't work for all parties: those who want the instances can have them, those who don't can "turn them off," and beginners can start with a prelude that turns them off also.

We will need to be clear about which packages (transitively) poison instances.

Tom


El 19 mar 2017, a las 19:21, Edward Kmett <[hidden email]> escribió:

Sadly, hlint remains fairly (er.. completely?) ignorant of the types of an expression. It has no idea what is in scope, and just provides syntax-directed guidance, so hlint isn't the answer here.

That said, there does seem to be a plausible solution available.

I have zero objection to adding {-# POISON #-} pragma that users could include in their own code that just makes it so that whatever instance is named inside of it becomes unusable. You could view it as setting up an ambiguous instance declaration that is currently impossible to write, so any attempt to use the instance would complain about ambiguous instances (or preferably about the instance being poisoned and name the source of the poison).

This would make the status quo the norm, but then you could modify your custom Prelude or what have you to {-# POISON instance Foldable ((,) e) #-} and then any code that transitively depended on your Prelude would complain if it attempted to use that instance.

Such a poison pragma should be sound with respect to GHC's internals. It is effectively making an extra instance just to cause conflicts later on in the instance selection process. This is the same as if we had Foldable the class being defined in one module, the real instance in another, someone's poison instance in a third, then a fourth module that imports the whole diamond. Any attempt to use the instance that is defined in two contradictory ways in that 4th module today will complain.

Folks who care about these instances would then have the choice about whether to avoid any package that poisoned any instances they cared about, and folks who fee strongly about this issue would be able to live in a world where these instances didn't affect any code they wrote and if they didn't want to turn off users that care about being poisoned, could turn on the poisoning through cabal flags so that they don't infect their API but get an opt-in internal consistency check for local compilation.

-Edward Kmett

P.S. The term poison above derives from the C convention of poisoning names you don't want to see in your code due to safety considerations. e.g. In GCC you can do so with

#pragma GCC poison printf sprintf fprintf


On Sat, Mar 18, 2017 at 8:51 PM, <[hidden email]> wrote:


> El 18 mar 2017, a las 16:01, Lana Black <[hidden email]> escribió:
>
>> On 18/03/17 19:49, Henning Thielemann wrote:
>>
>>> On Sat, 18 Mar 2017, Carter Schonwald wrote:
>>>
>>> for what?
>>
>> A warning if someone e.g. calls 'length (a,b)', or more generally, if
>> certain instances are used.
>> _______________________________________________
>> Libraries mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
> Please no. Many of us like our code Wall-clean while still being able to
> write polymorphic functions. Adding more warnings that are often
> triggered by correct code (redundant constraints, anyone?) only leads to
> more headache.
>
> You could make that an hlint rule on the other hand.

Can it be a hlint rule? It seems quite difficult to predict that "length" will not ever be passed e.g. a 2-tuple in the general case, within hlint.

I would also favor a warning, and happily have -Wall not include it (though I'd prefer inclusion).

Tom


> _______________________________________________
> Libraries mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Adam Bergmark-2
Ed, If i understand you correctly then this poisoning doesn't scale. If I want to use it in my library I'm also forcing all my users to avoid these functions and it would split hackage in half. Effectively I'd only be able to use it for private code and applications. Or am I misunderstanding?

You can already do this in poisoning in a solid way by defining e.g. `class Unsatisfiable; instance Unsatisfiable => Show (a -> b) where show = undefined` and not exporting Unsatisfiable.

Cheers,
Adam



On Mon, 20 Mar 2017 at 01:56 <[hidden email]> wrote:
Edward: this is clever! I can't at the moment see any reason this wouldn't work for all parties: those who want the instances can have them, those who don't can "turn them off," and beginners can start with a prelude that turns them off also.

We will need to be clear about which packages (transitively) poison instances.

Tom


El 19 mar 2017, a las 19:21, Edward Kmett <[hidden email]> escribió:

Sadly, hlint remains fairly (er.. completely?) ignorant of the types of an expression. It has no idea what is in scope, and just provides syntax-directed guidance, so hlint isn't the answer here.

That said, there does seem to be a plausible solution available.

I have zero objection to adding {-# POISON #-} pragma that users could include in their own code that just makes it so that whatever instance is named inside of it becomes unusable. You could view it as setting up an ambiguous instance declaration that is currently impossible to write, so any attempt to use the instance would complain about ambiguous instances (or preferably about the instance being poisoned and name the source of the poison).

This would make the status quo the norm, but then you could modify your custom Prelude or what have you to {-# POISON instance Foldable ((,) e) #-} and then any code that transitively depended on your Prelude would complain if it attempted to use that instance.

Such a poison pragma should be sound with respect to GHC's internals. It is effectively making an extra instance just to cause conflicts later on in the instance selection process. This is the same as if we had Foldable the class being defined in one module, the real instance in another, someone's poison instance in a third, then a fourth module that imports the whole diamond. Any attempt to use the instance that is defined in two contradictory ways in that 4th module today will complain.

Folks who care about these instances would then have the choice about whether to avoid any package that poisoned any instances they cared about, and folks who fee strongly about this issue would be able to live in a world where these instances didn't affect any code they wrote and if they didn't want to turn off users that care about being poisoned, could turn on the poisoning through cabal flags so that they don't infect their API but get an opt-in internal consistency check for local compilation.

-Edward Kmett

P.S. The term poison above derives from the C convention of poisoning names you don't want to see in your code due to safety considerations. e.g. In GCC you can do so with

#pragma GCC poison printf sprintf fprintf


On Sat, Mar 18, 2017 at 8:51 PM, <[hidden email]> wrote:


> El 18 mar 2017, a las 16:01, Lana Black <[hidden email]> escribió:
>
>> On 18/03/17 19:49, Henning Thielemann wrote:
>>
>>> On Sat, 18 Mar 2017, Carter Schonwald wrote:
>>>
>>> for what?
>>
>> A warning if someone e.g. calls 'length (a,b)', or more generally, if
>> certain instances are used.
>> _______________________________________________
>> Libraries mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
> Please no. Many of us like our code Wall-clean while still being able to
> write polymorphic functions. Adding more warnings that are often
> triggered by correct code (redundant constraints, anyone?) only leads to
> more headache.
>
> You could make that an hlint rule on the other hand.

Can it be a hlint rule? It seems quite difficult to predict that "length" will not ever be passed e.g. a 2-tuple in the general case, within hlint.

I would also favor a warning, and happily have -Wall not include it (though I'd prefer inclusion).

Tom


> _______________________________________________
> Libraries mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Artyom Kazak-2
In reply to this post by Tony Morris-4
On 20 Mar 2017 1:31 a.m., "Tony Morris" <[hidden email]> wrote:

"But the inconsistency with my intuition for the function named l-e-n-g-t-h that I learned in C programming school in the 1980s" is the best criteria that I have seen, and which commands outright dismissal.

This is exactly my criterion (except that I learned English, not C), and I do not agree with you that it "commands outright dismissal". There is value in functions being as general as possible, etc, but there is also value in conforming to human intuitions – because it prevents people from shooting themselves in their feet. Whether those intuitions come from math, or from C, or from English, or elsewhere, is entirely irrelevant. If behavior of `length` fully conforms to your intuitions, that's splendid and you're lucky; however, for me and my usecases it's *only* a source of pain, and I want a way to free myself of that pain (without having to learn another language or spend time trying to change my intuitions).

Wanting to make one's life easier is not an unreasonable desire.

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Tony Morris-4
What intuition exactly? Demonstrate it to me so that I may empathise with it. What pain? Show it to me. Are you sure it's intuition and not something else? How are you so sure?

On Mon, Mar 20, 2017 at 12:28 PM, Artyom Kazak <[hidden email]> wrote:
On 20 Mar 2017 1:31 a.m., "Tony Morris" <[hidden email]> wrote:

"But the inconsistency with my intuition for the function named l-e-n-g-t-h that I learned in C programming school in the 1980s" is the best criteria that I have seen, and which commands outright dismissal.

This is exactly my criterion (except that I learned English, not C), and I do not agree with you that it "commands outright dismissal". There is value in functions being as general as possible, etc, but there is also value in conforming to human intuitions – because it prevents people from shooting themselves in their feet. Whether those intuitions come from math, or from C, or from English, or elsewhere, is entirely irrelevant. If behavior of `length` fully conforms to your intuitions, that's splendid and you're lucky; however, for me and my usecases it's *only* a source of pain, and I want a way to free myself of that pain (without having to learn another language or spend time trying to change my intuitions).

Wanting to make one's life easier is not an unreasonable desire.



--

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

amindfv
In reply to this post by Adam Bergmark-2


El 19 mar 2017, a las 20:33, Adam Bergmark <[hidden email]> escribió:

Ed, If i understand you correctly then this poisoning doesn't scale. If I want to use it in my library I'm also forcing all my users to avoid these functions and it would split hackage in half. Effectively I'd only be able to use it for private code and applications. Or am I misunderstanding?

I think the idea is you can check that your library compiles cleanly with "-wpoison 'Foldable ((,) a)' " etc, but then you don't actually "put the poison in" your library (unless you feel that strongly about it)

Tom


You can already do this in poisoning in a solid way by defining e.g. `class Unsatisfiable; instance Unsatisfiable => Show (a -> b) where show = undefined` and not exporting Unsatisfiable.

Cheers,
Adam



On Mon, 20 Mar 2017 at 01:56 <[hidden email]> wrote:
Edward: this is clever! I can't at the moment see any reason this wouldn't work for all parties: those who want the instances can have them, those who don't can "turn them off," and beginners can start with a prelude that turns them off also.

We will need to be clear about which packages (transitively) poison instances.

Tom


El 19 mar 2017, a las 19:21, Edward Kmett <[hidden email]> escribió:

Sadly, hlint remains fairly (er.. completely?) ignorant of the types of an expression. It has no idea what is in scope, and just provides syntax-directed guidance, so hlint isn't the answer here.

That said, there does seem to be a plausible solution available.

I have zero objection to adding {-# POISON #-} pragma that users could include in their own code that just makes it so that whatever instance is named inside of it becomes unusable. You could view it as setting up an ambiguous instance declaration that is currently impossible to write, so any attempt to use the instance would complain about ambiguous instances (or preferably about the instance being poisoned and name the source of the poison).

This would make the status quo the norm, but then you could modify your custom Prelude or what have you to {-# POISON instance Foldable ((,) e) #-} and then any code that transitively depended on your Prelude would complain if it attempted to use that instance.

Such a poison pragma should be sound with respect to GHC's internals. It is effectively making an extra instance just to cause conflicts later on in the instance selection process. This is the same as if we had Foldable the class being defined in one module, the real instance in another, someone's poison instance in a third, then a fourth module that imports the whole diamond. Any attempt to use the instance that is defined in two contradictory ways in that 4th module today will complain.

Folks who care about these instances would then have the choice about whether to avoid any package that poisoned any instances they cared about, and folks who fee strongly about this issue would be able to live in a world where these instances didn't affect any code they wrote and if they didn't want to turn off users that care about being poisoned, could turn on the poisoning through cabal flags so that they don't infect their API but get an opt-in internal consistency check for local compilation.

-Edward Kmett

P.S. The term poison above derives from the C convention of poisoning names you don't want to see in your code due to safety considerations. e.g. In GCC you can do so with

#pragma GCC poison printf sprintf fprintf


On Sat, Mar 18, 2017 at 8:51 PM, <[hidden email]> wrote:


> El 18 mar 2017, a las 16:01, Lana Black <[hidden email]> escribió:
>
>> On 18/03/17 19:49, Henning Thielemann wrote:
>>
>>> On Sat, 18 Mar 2017, Carter Schonwald wrote:
>>>
>>> for what?
>>
>> A warning if someone e.g. calls 'length (a,b)', or more generally, if
>> certain instances are used.
>> _______________________________________________
>> Libraries mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
> Please no. Many of us like our code Wall-clean while still being able to
> write polymorphic functions. Adding more warnings that are often
> triggered by correct code (redundant constraints, anyone?) only leads to
> more headache.
>
> You could make that an hlint rule on the other hand.

Can it be a hlint rule? It seems quite difficult to predict that "length" will not ever be passed e.g. a 2-tuple in the general case, within hlint.

I would also favor a warning, and happily have -Wall not include it (though I'd prefer inclusion).

Tom


> _______________________________________________
> Libraries mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

amindfv
In reply to this post by Artyom Kazak-2


> El 19 mar 2017, a las 21:28, Artyom Kazak <[hidden email]> escribió:
> [...] Whether those intuitions come from math, or from C, or from English,

(or from versions of Haskell before ghc 7.10)
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Tony Morris-4
The length of ((,) a) has been 1 since a very long time before GHC 7.10.

I even remember writing the code, which is not a common thing, which in
general, turned into a project with hundreds of contributors. That
should end any appeal to popularity!


On 20/03/17 13:34, [hidden email] wrote:
>
>> El 19 mar 2017, a las 21:28, Artyom Kazak <[hidden email]> escribió:
>> [...] Whether those intuitions come from math, or from C, or from English,
> (or from versions of Haskell before ghc 7.10)



_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

signature.asc (499 bytes) Download Attachment
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

amindfv
In reply to this post by Tony Morris-4
Tony: I'll take you at your word that you really would like an example of why we have a bee in our bonnet, in order to understand better. Here's an example of a program that can cause pain:

avg l = sum l / fromIntegral (length l)

... elsewhere ...

f x = map (+1) x

...elsewhere...

y = avg $ f foo


Then we change the definition of f to contain some extra data:

f x = (map (+1) x, bar)

Without warning, we suddenly get "bar" instead of the average we expected, and it all typechecks. Just, the data we get is wrong. We may not even notice the data is wrong at first. This isn't hypothetical: I've been bitten by this more than once. Now I've started writing "(length::[a]->Int)", "(maximum::Ord a=>[a]->a)" etc so I don't shoot myself in the foot.

Hope this is helpful.
Tom


El 19 mar 2017, a las 21:30, Tony Morris <[hidden email]> escribió:

What intuition exactly? Demonstrate it to me so that I may empathise with it. What pain? Show it to me. Are you sure it's intuition and not something else? How are you so sure?

On Mon, Mar 20, 2017 at 12:28 PM, Artyom Kazak <[hidden email]> wrote:
On 20 Mar 2017 1:31 a.m., "Tony Morris" <[hidden email]> wrote:

"But the inconsistency with my intuition for the function named l-e-n-g-t-h that I learned in C programming school in the 1980s" is the best criteria that I have seen, and which commands outright dismissal.

This is exactly my criterion (except that I learned English, not C), and I do not agree with you that it "commands outright dismissal". There is value in functions being as general as possible, etc, but there is also value in conforming to human intuitions – because it prevents people from shooting themselves in their feet. Whether those intuitions come from math, or from C, or from English, or elsewhere, is entirely irrelevant. If behavior of `length` fully conforms to your intuitions, that's splendid and you're lucky; however, for me and my usecases it's *only* a source of pain, and I want a way to free myself of that pain (without having to learn another language or spend time trying to change my intuitions).

Wanting to make one's life easier is not an unreasonable desire.



--

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Artyom Kazak-2
In reply to this post by Tony Morris-4
On 20 Mar 2017 3:31 a.m., "Tony Morris" <[hidden email]> wrote:
What intuition exactly? Demonstrate it to me so that I may empathise with it. What pain? Show it to me. Are you sure it's intuition and not something else? How are you so sure?

Okay, I have to admit that in this particular instance it's not quite my intuition which causes the pain. Or rather, it's a different kind of intuition, which I have acquired from Haskell in particular – "I don't have to be particularly careful about minor refactorings, because if I change the type of one thing, the compiler will tell me what other things I have to change if I want to preserve the logic of my code". The behavior of 'length' breaks this intuition because "I'll just pass a tuple here instead of whatever I used before" stops being a (relatively) safe refactoring.

(I'm saying "relatively" because of the existence of other extremely polymorphic things – e.g. 'Aeson.encode'. Luckily, they're much less common than 'length'.)

(An example of a scenario which more closely fits what I originally had in mind: Set.size is O(1), but HashSet.size is O(n) – this one was pretty fun to hunt down when we found that our code was unexpectedly slow. Unfortunately, I can not adequately expand the intuition behind my initial judgment of "HashSet.size is surely fast", because it's hard to do at 4am.)

The pain is pretty easy to demonstrate: I had to waste a day recreating the Foldable/Traversable hierarchy in a custom prelude to ensure that nobody in my team would be bitten by a refactoring. It wasn't *that* painful, sure – but it's somewhat scary to think that there might be dozens of other refactor-unfriendly functions lurking in libraries we're using (and one day they will come to bite us). 

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Edward Kmett-2
In reply to this post by Adam Bergmark-2

On Sun, Mar 19, 2017 at 9:33 PM, Adam Bergmark <[hidden email]> wrote:
Ed, If i understand you correctly then this poisoning doesn't scale. If I want to use it in my library I'm also forcing all my users to avoid these functions and it would split hackage in half. Effectively I'd only be able to use it for private code and applications. Or am I misunderstanding?

This is why I mentioned at the end that if you're doing this for a library you probably only want to enable poisoning with a cabal build flag. That way you can use it to verify that you build clean even with the poison enabled without crippling your users. Think of it like -Werror. You don't ship cabal packages with it enabled but you often use it locally. For classroom teaching scenarios using a custom prelude that poisons things fits the bill. If you are concerned about certain instances leaking into your private code, you can also poison willy-nilly, and nobody needs care. If you poison behind a flag it doesn't leak out into the ecosystem, and if you choose not to poison behind a flag, then people can choose to accept or reject your poisoned instances on their own merits.

e.g. async had an instance of Monad for Concurrently that was incompatible with its Applicative until recently. Fortunately, Simon Marlow was willing to listen to reason. However, had he not been, it is a situation where I could bring myself to resort to poison. Er... that sounds rather more gruesome than intended. Sorry, Simon. :)

You can already do this in poisoning in a solid way by defining e.g. `class Unsatisfiable; instance Unsatisfiable => Show (a -> b) where show = undefined` and not exporting Unsatisfiable.

Yes, if you want to ensure that an instance is never defined, but that doesn't work for things where there is already an instance in scope. You can't retroactively poison Foldable ((,) e) with this technique, as the class and instance are defined together in the same module (or the data type and instance are), as they have to be to avoid orphans, and so you never get a chance to interject a conflicting poisonous instance without GHC telling you that your instance collides with the existing instance as an error.

-Edward

Cheers,
Adam



On Mon, 20 Mar 2017 at 01:56 <[hidden email]> wrote:
Edward: this is clever! I can't at the moment see any reason this wouldn't work for all parties: those who want the instances can have them, those who don't can "turn them off," and beginners can start with a prelude that turns them off also.

We will need to be clear about which packages (transitively) poison instances.

Tom


El 19 mar 2017, a las 19:21, Edward Kmett <[hidden email]> escribió:

Sadly, hlint remains fairly (er.. completely?) ignorant of the types of an expression. It has no idea what is in scope, and just provides syntax-directed guidance, so hlint isn't the answer here.

That said, there does seem to be a plausible solution available.

I have zero objection to adding {-# POISON #-} pragma that users could include in their own code that just makes it so that whatever instance is named inside of it becomes unusable. You could view it as setting up an ambiguous instance declaration that is currently impossible to write, so any attempt to use the instance would complain about ambiguous instances (or preferably about the instance being poisoned and name the source of the poison).

This would make the status quo the norm, but then you could modify your custom Prelude or what have you to {-# POISON instance Foldable ((,) e) #-} and then any code that transitively depended on your Prelude would complain if it attempted to use that instance.

Such a poison pragma should be sound with respect to GHC's internals. It is effectively making an extra instance just to cause conflicts later on in the instance selection process. This is the same as if we had Foldable the class being defined in one module, the real instance in another, someone's poison instance in a third, then a fourth module that imports the whole diamond. Any attempt to use the instance that is defined in two contradictory ways in that 4th module today will complain.

Folks who care about these instances would then have the choice about whether to avoid any package that poisoned any instances they cared about, and folks who fee strongly about this issue would be able to live in a world where these instances didn't affect any code they wrote and if they didn't want to turn off users that care about being poisoned, could turn on the poisoning through cabal flags so that they don't infect their API but get an opt-in internal consistency check for local compilation.

-Edward Kmett

P.S. The term poison above derives from the C convention of poisoning names you don't want to see in your code due to safety considerations. e.g. In GCC you can do so with

#pragma GCC poison printf sprintf fprintf


On Sat, Mar 18, 2017 at 8:51 PM, <[hidden email]> wrote:


> El 18 mar 2017, a las 16:01, Lana Black <[hidden email]> escribió:
>
>> On 18/03/17 19:49, Henning Thielemann wrote:
>>
>>> On Sat, 18 Mar 2017, Carter Schonwald wrote:
>>>
>>> for what?
>>
>> A warning if someone e.g. calls 'length (a,b)', or more generally, if
>> certain instances are used.
>> _______________________________________________
>> Libraries mailing list
>> [hidden email]
>> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
>
> Please no. Many of us like our code Wall-clean while still being able to
> write polymorphic functions. Adding more warnings that are often
> triggered by correct code (redundant constraints, anyone?) only leads to
> more headache.
>
> You could make that an hlint rule on the other hand.

Can it be a hlint rule? It seems quite difficult to predict that "length" will not ever be passed e.g. a 2-tuple in the general case, within hlint.

I would also favor a warning, and happily have -Wall not include it (though I'd prefer inclusion).

Tom


> _______________________________________________
> Libraries mailing list
> [hidden email]
> http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries


_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

AntC
In reply to this post by Andreas Abel
> On Thu Mar 2 16:30:21 UTC 2017 Henning Thielemann wrote:

>> On Thu, 2 Mar 2017, Andreas Abel wrote:

>> Today a student came to me wondering why a certain
function
>> produced a regular result, where he had expected an
error.  
>> Turned out he had used `concat`, but not on a lists of
lists
>> as he had thought, but on a lists of `Either a [b]`.

> That is, he did (concat (xs :: [Either a [b]]))?
> Does GHC actually accept that?
> If at all, it would not have to do with Foldable.

Good questions Henning. They've got swamped.
I found Andreas' description a bit unclear.

We have:
    concat :: Foldable t => t [a] -> [a]

So the `concat` you put above would give an error.

To get a result, the student must have put
    (concat (xs :: Either a [b]))
And got result either `[]` from (Left _)
or a genuine List from (Right ys).

Either way, I'd be grumpy: this hasn't and couldn't
concatenate anything.

Two thoughts:
1) The FTP hasn't done a very thorough job.
     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

2) It's obvious why FTP didn't go that far:
     (concat (xs :: [Either a [b]])) -- as you speculated --
would be daft.
     concat only applies for structures that can be
concatenated.
     But that surely includes more than Lists(?)
     So this is essentially the same complaint as for
`length`:
     Why have an instance of Foldable which always returns
length 1?
     (Or for some instances returns length 0 or 1.)

`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.

Note that `length` used not to be in Foldable.
`concat` was, but restricted to Lists, as now.

If there's a need for a method in Foldable
to return 1 for a tuple, or 0..1 for Maybe/Either,
don't call it `length`, call it `cardinality` (say).
(This is the same approach as for separate `map`, `fmap`,
`foldMap`.)

That isn't breaking genericity/polymorphism/library
rationalisation,
`length` was never previously available for non-List
Foldables.
So no code could have been using it.
`concat` was always in the wrong place,
but Foldable (when it was a bit of a rag-bag) used to be
good enough.


AntC
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Edward Kmett-2
On Thu, Mar 23, 2017 at 6:18 AM, Anthony Clayden <[hidden email]> 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. 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.

Prelude Data.Foldable> :t concat
concat :: Foldable t => t [a] -> [a]
Prelude Data.Foldable> :t fold
fold :: (Monoid m, Foldable t) => t m -> m

concat is just a type restricted version of fold = foldMap id provided basically for legacy compatibility.

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. =)

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.

     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, you can use and for many container types it'll compute the length in O(1) rather than a separate pass.

Without length as a member of Foldable then we must always do two passes through the container.

toVector :: Foldable f => f a -> Vector a
toVector xs = Vector.fromListN (getSum $ foldMap (\_ -> Sum 1) xs) (toList xs)

or rely on Vector internally repeatedly doubling the guessed at size of the vector and trimming at the end.

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.

`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 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.

Note that `length` used not to be in Foldable.
`concat` was, but restricted to Lists, as now.

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. 

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. 

With the choices made all of the "mainstream" base modules now have no name conflicts and can be imported unqualified.

If there's a need for a method in Foldable
to return 1 for a tuple, or 0..1 for Maybe/Either,
don't call it `length`, call it `cardinality` (say).
(This is the same approach as for separate `map`, `fmap`,
`foldMap`.)
 
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 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.

-Edward

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

AntC
In reply to this post by Andreas Abel
> 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

Again, no: nobody's code could have been using
Foldable length.

> and folks happen to be using all the "good" names for such
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
_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Deprecate Foldable for Either

Edward Kmett-2
On Fri, Mar 24, 2017 at 5:17 AM, Anthony Clayden <[hidden email]> wrote:
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.

Sure. There are plenty of them to poke at. It was a big complicated messy process. I have plenty of warts I would have liked to have shaved off, but which we couldn't justify because of costs in other areas.
 
> 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 [...]) ?

Because it was already there in Foldable along side a number of other combinators that are already being generalized, and there was no good reason to explicitly _exclude_ it.
 
> 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.

length/null were generalized by a separate proposal to the FTP that happened at the same time. 

Nobody put forth such a proposal to generalize concat.

> 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.

No, I clarified that length/null generalization was a separate proposal, just later in my reply.
 
I'm not assuming malice or dissimilation on your part. Please do not assume it on mine

> 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`.

This runs afoul of the concern with taking new names from the Prelude which was a very heavy concern at the time.

> That is another color the bikeshed could have been
colored,

That's an arrogant comment.

It wasn't dismissive. I was acknowledging that it was a different way things could have gone. We decided against it because of other concerns. There are lots of parts to this proposal that could have been concocted differently. And lots of reasons to favor one variation on the theme over another.
 
> 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.

  
> 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.


I was conveying the process by which extra surprising members wound up in Foldable at all, it doesn't directly apply to length and I was not trying to say that it did. I was trying to say why we wound up with things like sum, etc. in the class as a preamble to this next paragraph.
 
> 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.

size would have involved taking a fresh name. 

> 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?

"Using it for a decade" is a short hand for saying that the bulk of the Foldable machinery had existed unchanged for a very long time. The proposal wasn't initially intended to break all that much new ground.

Again length/null generalization was a separate proposal that happened at the same time as FTP proper.

> 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.


Yes, and sum in Prelude previously only worked on lists, etc.

Little existing code was broken by the change due to it being a strict generalization. I can't say none, because length "foo" with OverloadedStrings broke.

If I had it to do over again? I probably would have bitten the bullet and had us take a fresh name. The outcry over fresh names turned out to be smaller than intended and the outcry over length much larger. That isn't to say I'm in a hurry to go through a second wave of breakage that churns the waters twice to fix it at this point.
 
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?

Yes. Once you understand what Foldable is and must mean. I even _use_ these facts in concrete code that deliberately knows nothing about the Foldable/Traversable container over which it is working, when that code is put into a larger context.
 
Note: "length", not "size".

The name could well have been size. It was a very near thing. We let the hard rule about new unnecessary names shape the proposal very heavily. In retrospect it may well have been better to accept some new names. This prevented re-export of a lot of combinators in Data.Traversable as well.

However, by the time such discussion could have happened everything got caught up in the drama of the massive up/down vote that was put up as a last-ditch effort to stop the whole thing. No such nuanced discussion could be heard through that noise.

I'm simply asking you to accept that there are some reasons on the other side o the "length" vs. "cardinality" name choice and that there is a reasonable scope for debate about which is the best to go with. The alternative was very close to having been chosen, but wasn't for the reasons I enumerated above and before.

I simply don't have a time machine, and the amount of pain involved in thrashing this design _now_ would be rather disproportionate.

-Edward

_______________________________________________
Libraries mailing list
[hidden email]
http://mail.haskell.org/cgi-bin/mailman/listinfo/libraries
1234
Loading...