Hi guys, Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences: #sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections #detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary) #sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though. Benchmarks: [ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096 Can we settle for a single implementation? Such as (modified from #sumNumbers:): newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ] This implementation combines the best of the three implementations I think. Benchmark: [ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955 BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as sum ^ self sum: [ :x | x ] As for the name for the new method, I suggest #sum:. Cheers, Max
Hi Max, Interesting results... replacing the #yourself in newSum: cuts the runtime by a factor of two. using an [:x | x ] block cost 10% compared to a direct version of sum. So the fastest sum is: sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ] And sum ^ self sum: [:x | x ] is 10% slower. And sum ^ self sum: #yourself is 100% slower (i.e. x2) It may be wise to avoid using #yourself for a block. Thierry 2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>:
Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element. I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of two. using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why? cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of two. using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
Note that the sum with #anyOne is wrong. #(1 2 3 4 5) inject: #(1 2 3 4 5) anyOne into: [ :sum :each | sum + each ] returns 16 instead of 15. Thierry 2015-12-01 10:18 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why? cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com>
wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of two. using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne
])
into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which
accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus
fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a
lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for
numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun
42391
[ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
On 01 Dec 2015, at 10:24, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Note that the sum with #anyOne is wrong.
#(1 2 3 4 5) inject: #(1 2 3 4 5) anyOne into: [ :sum :each | sum + each ]
returns 16 instead of 15.
You have subtract the element you picked with #anyOne !
Thierry
2015-12-01 10:18 GMT+01:00 Ben Coman <btc@openinworld.com>: On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why? cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of two. using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
2015-12-01 10:26 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
On 01 Dec 2015, at 10:24, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Note that the sum with #anyOne is wrong.
#(1 2 3 4 5) inject: #(1 2 3 4 5) anyOne into: [ :sum :each | sum + each ]
returns 16 instead of 15.
You have subtract the element you picked with #anyOne !
I think he means the suggested implementation for newSum is wrong, just because of this reason.
Thierry
2015-12-01 10:18 GMT+01:00 Ben Coman <btc@openinworld.com>: On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why? cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com>
wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of
two.
using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
2015-12-01 10:29 GMT+01:00 Nicolai Hess <nicolaihess@gmail.com>:
2015-12-01 10:26 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
On 01 Dec 2015, at 10:24, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Note that the sum with #anyOne is wrong.
#(1 2 3 4 5) inject: #(1 2 3 4 5) anyOne into: [ :sum :each | sum + each ]
returns 16 instead of 15.
You have subtract the element you picked with #anyOne !
I think he means the suggested implementation for newSum is wrong, just because of this reason.
Exactly ;)
Thierry
On 01 Dec 2015, at 10:18, Ben Coman <btc@openinworld.com> wrote:
On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why?
How many times have you summed a collection of things, where (1) you could not start from the number 0 and (2) the operation was #+ ? Me, never, and if I would need that, I would do an #inject:into: myself. Any counter examples that occur a lot ?
cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of two. using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
2015-12-01 10:26 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
On 01 Dec 2015, at 10:18, Ben Coman <btc@openinworld.com> wrote:
On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why?
How many times have you summed a collection of things, where (1) you could not start from the number 0 and (2) the operation was #+ ? Me, never, and if I would need that, I would do an #inject:into: myself. Any counter examples that occur a lot ?
wokring with things like Color (Color wheel:10) sum "works" (Color wheel:10) newSum:#yourself " would not work"
cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com>
wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of two. using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne
])
into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which
accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will
thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a
lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for
numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ]
timeToRun 42391
[ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
The basic question for me is, what should #() sum return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it. If we agree on that, we could change the #sum to | sum sample | self isEmpty ifTrue: [ ^ 0 ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample And the same for #sum: As for #detectSum: that should be removed I think.
On 01 Dec 2015, at 10:33, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-01 10:26 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
On 01 Dec 2015, at 10:18, Ben Coman <btc@openinworld.com> wrote:
On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why?
How many times have you summed a collection of things, where (1) you could not start from the number 0 and (2) the operation was #+ ? Me, never, and if I would need that, I would do an #inject:into: myself. Any counter examples that occur a lot ?
wokring with things like Color
(Color wheel:10) sum "works" (Color wheel:10) newSum:#yourself " would not work"
cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com> wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of two. using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
2015-12-01 11:46 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
Yes, this is just that a 0 value is returned for an empty collection, and, if you're expecting a sum of Colors, then this is a bit strange. Returning an error on a sum for an empty collection is maybe an overall cleaner concept Which can be expressed as: I prefer this code: aCollectionOfColors isEmpty ifTrue: [ sumOfColors := aDefaultColor ] ifFalse: [ sumOfColors := aCollectionOfColors sum ] To this code: sumOfColors := aCollectionOfColors sum. sumOfColors = 0 ifTrue: [ sumOfColors := aDefaultColor ]
If we agree on that, we could change the #sum to
| sum sample | self isEmpty ifTrue: [ ^ 0 ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
Ok, now I really like this implementation. Elegant. Thierry
And the same for #sum:
As for #detectSum: that should be removed I think.
On 01 Dec 2015, at 10:33, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-01 10:26 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
On 01 Dec 2015, at 10:18, Ben Coman <btc@openinworld.com> wrote:
On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why?
How many times have you summed a collection of things, where (1) you could not start from the number 0 and (2) the operation was #+ ? Me, never, and if I would need that, I would do an #inject:into: myself. Any counter examples that occur a lot ?
wokring with things like Color
(Color wheel:10) sum "works" (Color wheel:10) newSum:#yourself " would not work"
cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com>
wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of
two.
using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
On 12/01, Thierry Goubier wrote:
2015-12-01 11:46 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
Yes, this is just that a 0 value is returned for an empty collection, and, if you're expecting a sum of Colors, then this is a bit strange.
Returning an error on a sum for an empty collection is maybe an overall cleaner concept
Which can be expressed as:
I prefer this code:
aCollectionOfColors isEmpty ifTrue: [ sumOfColors := aDefaultColor ] ifFalse: [ sumOfColors := aCollectionOfColors sum ]
To this code:
sumOfColors := aCollectionOfColors sum. sumOfColors = 0 ifTrue: [ sumOfColors := aDefaultColor ]
or sumOfColors := (aCollectionOfColors ifEmpty: [ {aDefaultColor} ]) sum.
If we agree on that, we could change the #sum to
| sum sample | self isEmpty ifTrue: [ ^ 0 ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
Ok, now I really like this implementation. Elegant.
Why the temp variables? self ifEmpty: [ ^ 0 ]. ^ self inject: self anyOne negated into: [ :sum :each | sum + each ] But more importantly, if the empty/default is 0 then what is the reason for #anyOne? Otherwise you are mixing things. self ifEmpty: [ ^ 0 ]. ^ self inject: 0 into: [ :sum :each | sum + each ]. Peter
Thierry
And the same for #sum:
As for #detectSum: that should be removed I think.
On 01 Dec 2015, at 10:33, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-01 10:26 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
On 01 Dec 2015, at 10:18, Ben Coman <btc@openinworld.com> wrote:
On Tue, Dec 1, 2015 at 4:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
I am all for a cleanup, the current situation is confusing. The basic #sum should be fast AND work for empty collections with 0 as starting element.
I know why the #anyOne is used, and that use case should be preserved, but it is less common IMHO.
I'm curious to be learn why?
How many times have you summed a collection of things, where (1) you could not start from the number 0 and (2) the operation was #+ ? Me, never, and if I would need that, I would do an #inject:into: myself. Any counter examples that occur a lot ?
wokring with things like Color
(Color wheel:10) sum "works" (Color wheel:10) newSum:#yourself " would not work"
cheers -ben
On 01 Dec 2015, at 09:38, Thierry Goubier <thierry.goubier@gmail.com>
wrote:
Hi Max,
Interesting results...
replacing the #yourself in newSum: cuts the runtime by a factor of
two.
using an [:x | x ] block cost 10% compared to a direct version of sum.
So the fastest sum is:
sum ^ self inject: (self ifEmpty: [ ^ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + each ]
And
sum ^ self sum: [:x | x ]
is 10% slower.
And
sum ^ self sum: #yourself
is 100% slower (i.e. x2)
It may be wise to avoid using #yourself for a block.
Thierry
2015-12-01 9:17 GMT+01:00 Max Leske <maxleske@gmail.com>: Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
-- Peter
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol. It shows the need for #sum:ifEmpty: though Stephan
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0 I want the simplest case to be simple, having a non-0 default is a special case IMHO
Stephan
Well, Iâm glad I've struck a nerve there :) I agree that signalling an exception isnât wrong, itâs just annoying because you have to do the check manually each time. I think I like #sum:ifEmpty:. When you look through the methods in Collection youâll see #sum, #sum: and #sum:ifEmpty together in one place and it will be clear how to use them.
On 01 Dec 2015, at 12:52, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
+1
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
+100 That allows for nice Smalltalk like âmyNumbers sum isZero â¦â.
I want the simplest case to be simple, having a non-0 default is a special case IMHO
Stephan
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers. We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers. Cheers, Doru
Stephan
-- www.tudorgirba.com "Speaking louder won't make the point worthier."
On 01 Dec 2015, at 15:11, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
But wouldnât it be nice to have #sum:ifEmpty:? I think that this is a nice way to describe summing. Otherwise I have to do ifEmpty: [ something ] ifNotEmpty: [ :col | col sum: ⦠] Cheers. Uko
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers.
We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers.
Cheers, Doru
Stephan
-- www.tudorgirba.com
"Speaking louder won't make the point worthier."
Hi,
On Dec 1, 2015, at 3:21 PM, Yuriy Tymchuk <yuriy.tymchuk@me.com> wrote:
On 01 Dec 2015, at 15:11, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
But wouldnât it be nice to have #sum:ifEmpty:? I think that this is a nice way to describe summing. Otherwise I have to do ifEmpty: [ something ] ifNotEmpty: [ :col | col sum: ⦠]
Sure. That would be fine, and would leave the meaning of sum to work with non-numbers objects. But, of course, you can also use sumNumbers: which starts from 0 not from anyOne and you will need no ifEmpty:. Cheers, Doru
Cheers. Uko
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers.
We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers.
Cheers, Doru
Stephan
-- www.tudorgirba.com
"Speaking louder won't make the point worthier."
-- www.tudorgirba.com "There are no old things, there are only old ways of looking at them."
Doru,
On 01 Dec 2015, at 15:11, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
Why ? Please explain.
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers.
We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers.
It is not about numbers or not, you are still using #+ in your generic case, that is numeric in my book. It is about the zero element and how to deal with an empty collection. The current solution, which was indeed recently added, leaves the problem of what to do with an empty collection. There is no general solution, you have to specify a zero element. But for most people in most cases that will be effectively 0, so lets make the unary #sum respect that. (And unary #sum will also work for non-empty non-zero based objects). The less common case can then use the longer message.
Cheers, Doru
Stephan
-- www.tudorgirba.com
"Speaking louder won't make the point worthier."
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?). It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO. something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg. My two cents for this. Regards! Esteban A. Maringolo
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
Interesting thought!
My two cents for this.
Regards!
Esteban A. Maringolo
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object. And sum: should be generic because of its name.
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
Interesting thought!
+100 Doru
My two cents for this.
Regards!
Esteban A. Maringolo
-- www.tudorgirba.com "Every thing has its own flow."
On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers. cheers -ben
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
Interesting thought!
+100
Doru
My two cents for this.
Regards!
Esteban A. Maringolo
-- www.tudorgirba.com
"Every thing has its own flow."
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+ therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
cheers -ben
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com>
wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
Interesting thought!
+100
Doru
My two cents for this.
Regards!
Esteban A. Maringolo
-- www.tudorgirba.com
"Every thing has its own flow."
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said. I am not suggesting to stop using #anyOne because I like why it is there and what it can do. The change I want is what happens with an empty collection when using the simplest selector, #sum. I do not want to say to some collection of numbers #sumIfEmpty: [0], because summing numbers starting from zero is the most common case and everybody expects that, hence the unary selector fits. I want the less common cases to use the more complicated API, as in some collection of colors #sumIfEmpty: [ Color black ] In my book that is common sense API design. Doing that I do no take anything away, because today you already have to make sure the collection is not empty. The only change would be in the error behaviour. I think that is a reasonable price to pay. Instead of having #anyOne fail, it will say that #+ cannot add 0 to some object, and in a comment we can point to the alternative API. http://izquotes.com/quote/242740 right ?
cheers -ben
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
Interesting thought!
+100
Doru
My two cents for this.
Regards!
Esteban A. Maringolo
-- www.tudorgirba.com
"Every thing has its own flow."
On 02 Dec 2015, at 15:45, Sven Van Caekenberghe <sven@stfx.eu> wrote:
The only change would be in the error behaviour. I think that is a reasonable price to pay. Instead of having #anyOne fail, it will say that #+ cannot add 0 to some object, and in a comment we can point to the alternative API.
Make that: instead of having #anyOne fail (which is pretty obscure) it will return 0 which will then probably fail.
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition. https://www.google.com.au/search?q=define+sum&oq=define+sum https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate For example... * KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) . * String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error. So actually there seem some existing problems with summing non-numerics. What examples work? * Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits. * Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case. cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
I am not suggesting to stop using #anyOne because I like why it is there and what it can do.
The change I want is what happens with an empty collection when using the simplest selector, #sum.
I do not want to say to some collection of numbers #sumIfEmpty: [0], because summing numbers starting from zero is the most common case and everybody expects that, hence the unary selector fits.
I want the less common cases to use the more complicated API, as in some collection of colors #sumIfEmpty: [ Color black ]
In my book that is common sense API design.
Doing that I do no take anything away, because today you already have to make sure the collection is not empty.
The only change would be in the error behaviour. I think that is a reasonable price to pay. Instead of having #anyOne fail, it will say that #+ cannot add 0 to some object, and in a comment we can point to the alternative API.
http://izquotes.com/quote/242740 right ?
cheers -ben
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
Interesting thought!
+100
Doru
My two cents for this.
Regards!
Esteban A. Maringolo
-- www.tudorgirba.com
"Every thing has its own flow."
<uncontrolled snipping> On Thu, Dec 3, 2015 at 5:48 AM, Ben Coman <btc@openinworld.com> wrote:
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
but points aren't commutative: 2@2 + 1 " = 3@3" 1 + 2@2 " = 3@2" Of course, 0 wouldn't be an issue, unless you wanted to access x or y! -cbc
On Fri, Dec 4, 2015 at 3:26 AM, Chris Cunningham <cunningham.cb@gmail.com> wrote:
<uncontrolled snipping>
On Thu, Dec 3, 2015 at 5:48 AM, Ben Coman <btc@openinworld.com> wrote:
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
but points aren't commutative:
2@2 + 1 " = 3@3" 1 + 2@2 " = 3@2"
Of course, 0 wouldn't be an issue, unless you wanted to access x or y!
-cbc
whoops? 1 + (2@2) = 3@3 cheers -ben
On Thu, Dec 3, 2015 at 4:51 PM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Dec 4, 2015 at 3:26 AM, Chris Cunningham <cunningham.cb@gmail.com> wrote:
<uncontrolled snipping>
On Thu, Dec 3, 2015 at 5:48 AM, Ben Coman <btc@openinworld.com> wrote:
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
but points aren't commutative:
2@2 + 1 " = 3@3" 1 + 2@2 " = 3@2"
Of course, 0 wouldn't be an issue, unless you wanted to access x or y!
-cbc
whoops? 1 + (2@2) = 3@3
cheers -ben
Yeah. Forgot the parenthesis. 1 + 2@2 is the same as (1 + 2) @ 2. My eye sees the closeness of 2@2 as a point - not as a binary message (which it obviously is).
So, never mind. -cbc
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com>
wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+sum&oq=define+sum https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value
therefore you *need* to work with #anyOne
and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
I am not suggesting to stop using #anyOne because I like why it is there
and what it can do.
The change I want is what happens with an empty collection when using
the simplest selector, #sum.
I do not want to say to some collection of numbers #sumIfEmpty: [0],
because summing numbers starting from zero is the most common case and everybody expects that, hence the unary selector fits.
I want the less common cases to use the more complicated API, as in some
collection of colors #sumIfEmpty: [ Color black ]
In my book that is common sense API design.
Doing that I do no take anything away, because today you already have to
make sure the collection is not empty.
The only change would be in the error behaviour. I think that is a
reasonable price to pay. Instead of having #anyOne fail, it will say that #+ cannot add 0 to some object, and in a comment we can point to the alternative API.
http://izquotes.com/quote/242740 right ?
cheers -ben
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <
emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to
delegate
this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
Interesting thought!
+100
Doru
My two cents for this.
Regards!
Esteban A. Maringolo
-- www.tudorgirba.com
"Every thing has its own flow."
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+sum&oq=define+sum
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like... Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample ArithmeticZero class >> + anObject ^anObject cheers -ben
I am not suggesting to stop using #anyOne because I like why it is there and what it can do.
The change I want is what happens with an empty collection when using the simplest selector, #sum.
I do not want to say to some collection of numbers #sumIfEmpty: [0], because summing numbers starting from zero is the most common case and everybody expects that, hence the unary selector fits.
I want the less common cases to use the more complicated API, as in some collection of colors #sumIfEmpty: [ Color black ]
In my book that is common sense API design.
Doing that I do no take anything away, because today you already have to make sure the collection is not empty.
The only change would be in the error behaviour. I think that is a reasonable price to pay. Instead of having #anyOne fail, it will say that #+ cannot add 0 to some object, and in a comment we can point to the alternative API.
http://izquotes.com/quote/242740 right ?
cheers -ben
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
On Fri, Dec 4, 2015 at 3:26 AM, Chris Cunningham <cunningham.cb@gmail.com> wrote:
<uncontrolled snipping>
On Thu, Dec 3, 2015 at 5:48 AM, Ben Coman <btc@openinworld.com> wrote:
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
but points aren't commutative:
2@2 + 1 " = 3@3" 1 + 2@2 " = 3@2"
Of course, 0 wouldn't be an issue, unless you wanted to access x or y!
On 04 Dec 2015, at 01:49, Ben Coman <btc@openInWorld.com> wrote:
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com <mailto:nicolaihess@gmail.com>> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I agree. If I want to sum something other than a collection of numbers Iâll happily provide the block for that summation (I wouldnât even expect strings to be summable to be honest. Why would you have a collection of numbers that are stringsâ¦? And if you really have one, you should convert it explicitly so that another person reading your code knows whatâs going on.)
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+sum&oq=define+sum
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
ArithmeticZero class >> + anObject ^anObject
While I think you might be on to something, I think we should take small steps. Iâd be happy already if we can just get rid of one superfluous method and provide a better API without starting to think about the deeper semantics. Also, I think thereâs a reason why Aconcagua is an external package: developers usually are happy to work with numbers (which are already objects in Smalltalk anyway) and theyâre fast.
cheers -ben
I am not suggesting to stop using #anyOne because I like why it is there and what it can do.
The change I want is what happens with an empty collection when using the simplest selector, #sum.
I do not want to say to some collection of numbers #sumIfEmpty: [0], because summing numbers starting from zero is the most common case and everybody expects that, hence the unary selector fits.
I want the less common cases to use the more complicated API, as in some collection of colors #sumIfEmpty: [ Color black ]
In my book that is common sense API design.
Doing that I do no take anything away, because today you already have to make sure the collection is not empty.
The only change would be in the error behaviour. I think that is a reasonable price to pay. Instead of having #anyOne fail, it will say that #+ cannot add 0 to some object, and in a comment we can point to the alternative API.
http://izquotes.com/quote/242740 right ?
cheers -ben
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
On Fri, Dec 4, 2015 at 3:26 AM, Chris Cunningham <cunningham.cb@gmail.com <mailto:cunningham.cb@gmail.com>> wrote:
<uncontrolled snipping>
On Thu, Dec 3, 2015 at 5:48 AM, Ben Coman <btc@openinworld.com <mailto:btc@openinworld.com>> wrote:
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
but points aren't commutative:
2@2 + 1 " = 3@3" 1 + 2@2 " = 3@2"
Of course, 0 wouldn't be an issue, unless you wanted to access x or y!
Ben, _,,,^..^,,,_ (phone)
On Dec 4, 2015, at 12:49 AM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+sum&oq=define+sum
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
ArithmeticZero class >> + anObject ^anObject
surely you mean Collection>>sum ^self inject: ArithmeticZero into: [ :accum :each | accum + each ] ArithmeticZero class >> + anObject ^anObject and I'd optimise as Collection>>sum | sum | sum := ArithmeticZero. self do: [ :each | sum := sum + each ]. ^sum But in with those that want an error and would use either Collection>>sum ^self inject: self anyOne species zero into: [ :accum :each | accum + each ] or Collection>>sum | sum | sum := self anyOne species zero. self do: [ :each | sum := sum + each ]. ^sum
cheers -ben
I am not suggesting to stop using #anyOne because I like why it is there and what it can do.
The change I want is what happens with an empty collection when using the simplest selector, #sum.
I do not want to say to some collection of numbers #sumIfEmpty: [0], because summing numbers starting from zero is the most common case and everybody expects that, hence the unary selector fits.
I want the less common cases to use the more complicated API, as in some collection of colors #sumIfEmpty: [ Color black ]
In my book that is common sense API design.
Doing that I do no take anything away, because today you already have to make sure the collection is not empty.
The only change would be in the error behaviour. I think that is a reasonable price to pay. Instead of having #anyOne fail, it will say that #+ cannot add 0 to some object, and in a comment we can point to the alternative API.
http://izquotes.com/quote/242740 right ?
cheers -ben
On 01 Dec 2015, at 15:31, Esteban A. Maringolo <emaringolo@gmail.com> wrote:
I don't want to be heretic (or too orthodox), but why not to delegate this behavior to other class (an iterator maybe?).
It's too tempting adding these convenience methods to Collection and/or subclasses, but anything that requires an explicit protocol of its elements is wrong, IMO.
something like aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
On Fri, Dec 4, 2015 at 3:26 AM, Chris Cunningham <cunningham.cb@gmail.com> wrote:
<uncontrolled snipping>
On Thu, Dec 3, 2015 at 5:48 AM, Ben Coman <btc@openinworld.com> wrote: * Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
but points aren't commutative:
2@2 + 1 " = 3@3" 1 + 2@2 " = 3@2"
Of course, 0 wouldn't be an issue, unless you wanted to access x or y!
2015-12-25 2:03 GMT+01:00 Eliot Miranda <eliot.miranda@gmail.com>:
Ben,
_,,,^..^,,,_ (phone)
On Dec 4, 2015, at 12:49 AM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com>
wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial
value
therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
ArithmeticZero class >> + anObject ^anObject
surely you mean
Collection>>sum ^self inject: ArithmeticZero into: [ :accum :each | accum + each ]
ArithmeticZero class >> + anObject ^anObject
and I'd optimise as Collection>>sum | sum | sum := ArithmeticZero. self do: [ :each | sum := sum + each ]. ^sum
But in with those that want an error and would use either Collection>>sum ^self inject: self anyOne species zero into: [ :accum :each | accum + each ]
or
Collection>>sum | sum | sum := self anyOne species zero. self do: [ :each | sum := sum + each ]. ^sum
Not bad, but the class cannot allways determine the correct zero. For example Matrix is too generic and can't guess about dimensions which are instance specific. So zero should be instance specific. Nicolas
On Sun, Dec 27, 2015 at 6:13 AM, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-25 2:03 GMT+01:00 Eliot Miranda <eliot.miranda@gmail.com>:
Ben,
_,,,^..^,,,_ (phone)
On Dec 4, 2015, at 12:49 AM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+sum&oq=define+sum
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
ArithmeticZero class >> + anObject ^anObject
surely you mean
Collection>>sum ^self inject: ArithmeticZero into: [ :accum :each | accum + each ]
ArithmeticZero class >> + anObject ^anObject
and I'd optimise as Collection>>sum | sum | sum := ArithmeticZero. self do: [ :each | sum := sum + each ]. ^sum
But in with those that want an error and would use either Collection>>sum ^self inject: self anyOne species zero into: [ :accum :each | accum + each ]
or
Collection>>sum | sum | sum := self anyOne species zero. self do: [ :each | sum := sum + each ]. ^sum
Not bad, but the class cannot allways determine the correct zero. For example Matrix is too generic and can't guess about dimensions which are instance specific. So zero should be instance specific.
So if "species zero" isn't good to implement summing, could we use it to delegate raising an error. For example, cases like Matrix where summing an empty collection should produce an error, this could be done by ArithmeticZero delegating to the second operand to raise the error there? ArithmeticZero >> + operand operand species zero + operand Matrix class >> zero ErrorZeroDoesn'tMakeSense new signal. ? cheers -ben
2015-12-27 2:49 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Sun, Dec 27, 2015 at 6:13 AM, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-25 2:03 GMT+01:00 Eliot Miranda <eliot.miranda@gmail.com>:
Ben,
_,,,^..^,,,_ (phone)
On Dec 4, 2015, at 12:49 AM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <
wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <
tudor@tudorgirba.com>
wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of
" {
KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2
sven@stfx.eu> +
1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not
assume
a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
ArithmeticZero class >> + anObject ^anObject
surely you mean
Collection>>sum ^self inject: ArithmeticZero into: [ :accum :each | accum + each ]
ArithmeticZero class >> + anObject ^anObject
and I'd optimise as Collection>>sum | sum | sum := ArithmeticZero. self do: [ :each | sum := sum + each ]. ^sum
But in with those that want an error and would use either Collection>>sum ^self inject: self anyOne species zero into: [ :accum :each | accum + each ]
or
Collection>>sum | sum | sum := self anyOne species zero. self do: [ :each | sum := sum + each ]. ^sum
Not bad, but the class cannot allways determine the correct zero. For example Matrix is too generic and can't guess about dimensions which are instance specific. So zero should be instance specific.
So if "species zero" isn't good to implement summing, could we use it to delegate raising an error. For example, cases like Matrix where summing an empty collection should produce an error, this could be done by ArithmeticZero delegating to the second operand to raise the error there?
ArithmeticZero >> + operand operand species zero + operand
Matrix class >> zero ErrorZeroDoesn'tMakeSense new signal.
? cheers -ben
No, becauseItMakesSenseAtInstanceSide, I'd rather say it like this: if we want to adopt injecting zero into the sum, then let's make it an instance side message rather than class side. It's a rather elegant solution, but remember only for a minor problem: - That's only necessary for unordered Collection (since Sequenceable can just inject self first and start looping at index 2). - And that does not solve the case of empty collections which cannot use anyOne Nicolas
On Dec 27, 2015, at 1:52 PM, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-27 2:49 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Sun, Dec 27, 2015 at 6:13 AM, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-25 2:03 GMT+01:00 Eliot Miranda <eliot.miranda@gmail.com>:
Ben,
_,,,^..^,,,_ (phone)
On Dec 4, 2015, at 12:49 AM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+sum&oq=define+sum
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
ArithmeticZero class >> + anObject ^anObject
surely you mean
Collection>>sum ^self inject: ArithmeticZero into: [ :accum :each | accum + each ]
ArithmeticZero class >> + anObject ^anObject
and I'd optimise as Collection>>sum | sum | sum := ArithmeticZero. self do: [ :each | sum := sum + each ]. ^sum
But in with those that want an error and would use either Collection>>sum ^self inject: self anyOne species zero into: [ :accum :each | accum + each ]
or
Collection>>sum | sum | sum := self anyOne species zero. self do: [ :each | sum := sum + each ]. ^sum
Not bad, but the class cannot allways determine the correct zero. For example Matrix is too generic and can't guess about dimensions which are instance specific. So zero should be instance specific.
So if "species zero" isn't good to implement summing, could we use it to delegate raising an error. For example, cases like Matrix where summing an empty collection should produce an error, this could be done by ArithmeticZero delegating to the second operand to raise the error there?
ArithmeticZero >> + operand operand species zero + operand
Matrix class >> zero ErrorZeroDoesn'tMakeSense new signal.
? cheers -ben
No, becauseItMakesSenseAtInstanceSide, I'd rather say it like this: if we want to adopt injecting zero into the sum, then let's make it an instance side message rather than class side.
+1, so it becomes eg Collection>>sum | sum | sum := self anyOne asZero. self do: [ :each | sum := sum + each ]. ^sum and a Matrix can respond to asZero by answering a suitably dimensioned zero instance. And if product were implemented, to asUnity with a suitable "one".
It's a rather elegant solution, but remember only for a minor problem: - That's only necessary for unordered Collection (since Sequenceable can just inject self first and start looping at index 2). - And that does not solve the case of empty collections which cannot use anyOne
I thought the conclusion was that empty collections /should/ error since the type of zero is undefined. So if anyOne errors and sum included in its comment an explanation of it being undefined if empty and why then we're good.
Nicolas
Well, I beg to disagree, #sum on an empty collection should return 0. https://en.wikipedia.org/wiki/Empty_sum We do not need more tricks, the current trick with #anyOne is more than enough, that is not the problem. The problem is that it is impossible to talk about sum (in the general most common sense) without assuming that the collection should have contained numbers, where 0 is the neutral element. The #anyOne trick solves the problem of the neutral element for non empty collection, but leaves it unsolved for empty collections. Defining it so that empty collections error is really silly: check the senders, most of them are then forced to check before calling #sum. In that case, they could all just as well have used #inject:into: for the same amount of code.
On 28 Dec 2015, at 21:08, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Dec 27, 2015, at 1:52 PM, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-27 2:49 GMT+01:00 Ben Coman <btc@openinworld.com>: On Sun, Dec 27, 2015 at 6:13 AM, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-25 2:03 GMT+01:00 Eliot Miranda <eliot.miranda@gmail.com>:
Ben,
_,,,^..^,,,_ (phone)
On Dec 4, 2015, at 12:49 AM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections.
I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name.
I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+
To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+sum&oq=define+sum
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value.
I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful.
It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
ArithmeticZero class >> + anObject ^anObject
surely you mean
Collection>>sum ^self inject: ArithmeticZero into: [ :accum :each | accum + each ]
ArithmeticZero class >> + anObject ^anObject
and I'd optimise as Collection>>sum | sum | sum := ArithmeticZero. self do: [ :each | sum := sum + each ]. ^sum
But in with those that want an error and would use either Collection>>sum ^self inject: self anyOne species zero into: [ :accum :each | accum + each ]
or
Collection>>sum | sum | sum := self anyOne species zero. self do: [ :each | sum := sum + each ]. ^sum
Not bad, but the class cannot allways determine the correct zero. For example Matrix is too generic and can't guess about dimensions which are instance specific. So zero should be instance specific.
So if "species zero" isn't good to implement summing, could we use it to delegate raising an error. For example, cases like Matrix where summing an empty collection should produce an error, this could be done by ArithmeticZero delegating to the second operand to raise the error there?
ArithmeticZero >> + operand operand species zero + operand
Matrix class >> zero ErrorZeroDoesn'tMakeSense new signal.
? cheers -ben
No, becauseItMakesSenseAtInstanceSide, I'd rather say it like this: if we want to adopt injecting zero into the sum, then let's make it an instance side message rather than class side.
+1, so it becomes eg
Collection>>sum | sum | sum := self anyOne asZero. self do: [ :each | sum := sum + each ]. ^sum
and a Matrix can respond to asZero by answering a suitably dimensioned zero instance. And if product were implemented, to asUnity with a suitable "one".
It's a rather elegant solution, but remember only for a minor problem: - That's only necessary for unordered Collection (since Sequenceable can just inject self first and start looping at index 2). - And that does not solve the case of empty collections which cannot use anyOne
I thought the conclusion was that empty collections /should/ error since the type of zero is undefined. So if anyOne errors and sum included in its comment an explanation of it being undefined if empty and why then we're good.
Nicolas
This is sounding like a type inference situation. On 12/28/2015 03:20 PM, Sven Van Caekenberghe wrote:
Well, I beg to disagree, #sum on an empty collection should return 0.
https://en.wikipedia.org/wiki/Empty_sum
We do not need more tricks, the current trick with #anyOne is more than enough, that is not the problem.
The problem is that it is impossible to talk about sum (in the general most common sense) without assuming that the collection should have contained numbers, where 0 is the neutral element.
The #anyOne trick solves the problem of the neutral element for non empty collection, but leaves it unsolved for empty collections.
Defining it so that empty collections error is really silly: check the senders, most of them are then forced to check before calling #sum. In that case, they could all just as well have used #inject:into: for the same amount of code.
On 28 Dec 2015, at 21:08, Eliot Miranda <eliot.miranda@gmail.com> wrote:
On Dec 27, 2015, at 1:52 PM, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-27 2:49 GMT+01:00 Ben Coman <btc@openinworld.com>: On Sun, Dec 27, 2015 at 6:13 AM, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-25 2:03 GMT+01:00 Eliot Miranda <eliot.miranda@gmail.com>:
Ben,
_,,,^..^,,,_ (phone)
On Dec 4, 2015, at 12:49 AM, Ben Coman <btc@openinworld.com> wrote:
On Fri, Dec 4, 2015 at 4:23 AM, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-03 14:48 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 10:45 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 02 Dec 2015, at 15:21, Nicolai Hess <nicolaihess@gmail.com> wrote:
2015-12-02 15:03 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 12:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 5:13 PM, Max Leske <maxleske@gmail.com> wrote:
@Doru Youâre missing the point: #anyOne *fails* for empty collections. I am not missing the point at all. I am saying that if you want sum: to be generic, it cannot assume a specific Zero object.
And sum: should be generic because of its name. I am missing understanding the other use cases. Can you describe further the generic nature of #sum & #sum: ? I would have thought by default they only applied to numbers.
sum can be applied to anything that supports #+, not only numbers sum: can be applied to any collection with a block that return some object that supports #+ To me this is a mis-application of polymorphism, that just because something responds to #+ it should be summable. We have overloaded the semantics of #+ to mean both numeric addition and concatenation/membership, but technically "summation" relates only to numeric addition.
I didn't wanted to argue for or against any change. I just wanted to clarify that there are situations in which a generic sum/sum: that throws an error on empty collections and don't assume a null value makes sense.
https://www.google.com.au/search?q=define+sum&oq=define+sum
https://www.google.com.au/search?q=define+concatenate&oq=define+concatenate
For example...
* KMModifier implements #+ so what is the expected semantic of " { KMModifier shift . KMModifier meta} sum " ? To me this is more of a concatenation/join/union facility rather than numeric addition. (btw, that expression actually fails since KMModifier does not understand minus #- ) .
* String implements #+ and " { '1' . '2' } sum " --> '3', so actually its doing numeric addition. However " { 'a' . 'b' } sum " produces an error.
So actually there seem some existing problems with summing non-numerics. What examples work?
* Trait classes implement both #+ and #- , but the semantic seems more to do with membership than numeric addition. I don't how how to produce an example of using #sum against traits.
* Points are summable " { 2@2 . 3@3 } " --> 5@5. But then " 2@2 + 1 " --> 3@3 , so " {} sum " returning 0 would seem to not cause any error in this case.
cheers -ben
therefore you can not assume 0 (<- a number) is a proper initial value therefore you *need* to work with #anyOne and as you can not assume a proper initial value, you can not assume a default value for empty collections -> it should throw an error. If you (the caller of the function) knows what to do with an empty collection you have to check, or call inject:into: directly, with a proper initial value. I am sorry but I am getting really tired of this, you should read what is being said.
do that change, I am not against it. Ben just asked for an example and I thought it would be helpful. It was helpful :) It evolved my thinking. Now thinking further, I wonder how returning 0 will work with applications using units like Aconcagua, and if it would over-complicate things to do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
ArithmeticZero class >> + anObject ^anObject surely you mean
Collection>>sum ^self inject: ArithmeticZero into: [ :accum :each | accum + each ]
ArithmeticZero class >> + anObject ^anObject
and I'd optimise as Collection>>sum | sum | sum := ArithmeticZero. self do: [ :each | sum := sum + each ]. ^sum
But in with those that want an error and would use either Collection>>sum ^self inject: self anyOne species zero into: [ :accum :each | accum + each ]
or
Collection>>sum | sum | sum := self anyOne species zero. self do: [ :each | sum := sum + each ]. ^sum
Not bad, but the class cannot allways determine the correct zero. For example Matrix is too generic and can't guess about dimensions which are instance specific. So zero should be instance specific.
So if "species zero" isn't good to implement summing, could we use it to delegate raising an error. For example, cases like Matrix where summing an empty collection should produce an error, this could be done by ArithmeticZero delegating to the second operand to raise the error there?
ArithmeticZero >> + operand operand species zero + operand
Matrix class >> zero ErrorZeroDoesn'tMakeSense new signal.
? cheers -ben
No, becauseItMakesSenseAtInstanceSide, I'd rather say it like this: if we want to adopt injecting zero into the sum, then let's make it an instance side message rather than class side.
+1, so it becomes eg
Collection>>sum | sum | sum := self anyOne asZero. self do: [ :each | sum := sum + each ]. ^sum
and a Matrix can respond to asZero by answering a suitably dimensioned zero instance. And if product were implemented, to asUnity with a suitable "one".
It's a rather elegant solution, but remember only for a minor problem: - That's only necessary for unordered Collection (since Sequenceable can just inject self first and start looping at index 2). - And that does not solve the case of empty collections which cannot use anyOne I thought the conclusion was that empty collections /should/ error since the type of zero is undefined. So if anyOne errors and sum included in its comment an explanation of it being undefined if empty and why then we're good.
Nicolas
-- Robert . .. ... ^,^
On Tue, Dec 1, 2015 at 10:24 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
On 01 Dec 2015, at 15:11, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
Why ? Please explain.
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers.
We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers.
It is not about numbers or not, you are still using #+ in your generic case, that is numeric in my book. It is about the zero element and how to deal with an empty collection.
The current solution, which was indeed recently added, leaves the problem of what to do with an empty collection. There is no general solution, you have to specify a zero element.
But for most people in most cases that will be effectively 0, so lets make the unary #sum respect that. (And unary #sum will also work for non-empty non-zero based objects). The less common case can then use the longer message.
Lovely little discussion which would be great for beginning OO programmers to see. The question of whether to return 0 for an empty collection is interesting. While other things can be summed, we tend to think of sums returning numbers. In that sense, 0 is a great default return. On the other hand, having a specific object for a generic sum function is problematic (+1). There is a real tension and that would be a great discussion for people to have. Either way, the need for sumIfEmpty: is obvious. Cheers, Jeff On Tue, Dec 1, 2015 at 11:19 AM Ben Coman <btc@openinworld.com> wrote:
On Tue, Dec 1, 2015 at 10:24 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
On 01 Dec 2015, at 15:11, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
Why ? Please explain.
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers.
We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers.
It is not about numbers or not, you are still using #+ in your generic case, that is numeric in my book. It is about the zero element and how to deal with an empty collection.
The current solution, which was indeed recently added, leaves the problem of what to do with an empty collection. There is no general solution, you have to specify a zero element.
But for most people in most cases that will be effectively 0, so lets make the unary #sum respect that. (And unary #sum will also work for non-empty non-zero based objects). The less common case can then use the longer message.
Hi,
On Dec 1, 2015, at 3:24 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
On 01 Dec 2015, at 15:11, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
Why ? Please explain.
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers.
We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers.
It is not about numbers or not, you are still using #+ in your generic case, that is numeric in my book. It is about the zero element and how to deal with an empty collection.
The current solution, which was indeed recently added, leaves the problem of what to do with an empty collection. There is no general solution, you have to specify a zero element.
Exactly.
But for most people in most cases that will be effectively 0, so lets make the unary #sum respect that. (And unary #sum will also work for non-empty non-zero based objects). The less common case can then use the longer message.
You said it correctly: âmostâ :). That is why, a generic meaning should be left generic and provide a solution that will work with âallâ cases :). Itâs a matter of choice, but sum: has a too generic meaning to have it confined to numbers. Cheers, Doru
Cheers, Doru
Stephan
-- www.tudorgirba.com
"Speaking louder won't make the point worthier."
-- www.tudorgirba.com "Next time you see your life passing by, say 'hi' and get to know her."
On 01 Dec 2015, at 17:43, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 3:24 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
On 01 Dec 2015, at 15:11, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
Why ? Please explain.
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers.
We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers.
It is not about numbers or not, you are still using #+ in your generic case, that is numeric in my book. It is about the zero element and how to deal with an empty collection.
The current solution, which was indeed recently added, leaves the problem of what to do with an empty collection. There is no general solution, you have to specify a zero element.
Exactly.
But for most people in most cases that will be effectively 0, so lets make the unary #sum respect that. (And unary #sum will also work for non-empty non-zero based objects). The less common case can then use the longer message.
You said it correctly: âmostâ :). That is why, a generic meaning should be left generic and provide a solution that will work with âallâ cases :).
Itâs a matter of choice, but sum: has a too generic meaning to have it confined to numbers.
I think we all mean the same thing and understand each other's point. But the first meaning of 'sum' in Wikipedia leads to https://en.wikipedia.org/wiki/Summation which talks about numbers. I studied partly Mathematics, so I know there are different numeric systems and abstractions. My point is: the common case is adding (simple) numbers, the special case is the more general one, hence the best selector, #sum, should work for the common case, including for the empty one. Consider also Ben's link: https://en.wikipedia.org/wiki/Empty_sum Hence my suggestion to change #sum to | sum sample | self isEmpty ifTrue: [ ^ 0 ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample And the same for #sum: Note that the above *will* work for all like today, just not if they are empty. Now, of course we will need the ifEmpty: variants for what I consider the less common cases. Can I also remark that both #inject:into: and #fold: are perfectly fine for more involved cases. We have a lot of API ;-)
Cheers, Doru
Cheers, Doru
Stephan
-- www.tudorgirba.com
"Speaking louder won't make the point worthier."
-- www.tudorgirba.com
"Next time you see your life passing by, say 'hi' and get to know her."
Even though we havenât been able to completely agree on the resolution, I feel we can agree that we need to change something. Iâve opened an issue and copied the messages from this thread into it for documentation. https://pharo.fogbugz.com/f/cases/17174/Unify-sum-sum-sumNumbers-and-detectS... Cheers, Max
On 01 Dec 2015, at 18:41, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 17:43, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 3:24 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
On 01 Dec 2015, at 15:11, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 1, 2015, at 12:52 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol.
I hate the error, a lot ;-)
It shows the need for #sum:ifEmpty: though
Yes, as long as #() sum == 0
That wonât work :).
Why ? Please explain.
I want the simplest case to be simple, having a non-0 default is a special case IMHO
That is why you have sumNumbers:. We could also add Collection>>sumNumbers.
We had this discussion at length before Pharo 4, and this is when we agreed to add sumNumbers: and let sum: be generic (like the name says it should be) and not assume that it should work with Numbers.
It is not about numbers or not, you are still using #+ in your generic case, that is numeric in my book. It is about the zero element and how to deal with an empty collection.
The current solution, which was indeed recently added, leaves the problem of what to do with an empty collection. There is no general solution, you have to specify a zero element.
Exactly.
But for most people in most cases that will be effectively 0, so lets make the unary #sum respect that. (And unary #sum will also work for non-empty non-zero based objects). The less common case can then use the longer message.
You said it correctly: âmostâ :). That is why, a generic meaning should be left generic and provide a solution that will work with âallâ cases :).
Itâs a matter of choice, but sum: has a too generic meaning to have it confined to numbers.
I think we all mean the same thing and understand each other's point.
But the first meaning of 'sum' in Wikipedia leads to https://en.wikipedia.org/wiki/Summation which talks about numbers. I studied partly Mathematics, so I know there are different numeric systems and abstractions. My point is: the common case is adding (simple) numbers, the special case is the more general one, hence the best selector, #sum, should work for the common case, including for the empty one.
Consider also Ben's link:
https://en.wikipedia.org/wiki/Empty_sum
Hence my suggestion to change #sum to
| sum sample | self isEmpty ifTrue: [ ^ 0 ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
And the same for #sum:
Note that the above *will* work for all like today, just not if they are empty. Now, of course we will need the ifEmpty: variants for what I consider the less common cases.
Can I also remark that both #inject:into: and #fold: are perfectly fine for more involved cases. We have a lot of API ;-)
Cheers, Doru
Cheers, Doru
Stephan
-- www.tudorgirba.com
"Speaking louder won't make the point worthier."
-- www.tudorgirba.com
"Next time you see your life passing by, say 'hi' and get to know her."
#sum:ifEmpty: looks nice
On 01 Dec 2015, at 12:45, Stephan Eggermont <stephan@stack.nl> wrote:
On 01-12-15 11:46, Sven Van Caekenberghe wrote:
The basic question for me is, what should
#() sum
return. Right now, it is an error, I would very much like that for this common case the result would be 0. There is a lot of power (easy of use) in a unary selector, we should not destroy that with semantics that force a test before using it.
I like the error, it aligns with most of our collection protocol. It shows the need for #sum:ifEmpty: though
Stephan
The differences between #sum: and #sumNumbers: are well described in the respective method comments, but I agree that this may be a problem as most people will go for #sum: even for basic numeric collections. (And I personally got bit by this.) So maybe #sum: could adopt #sumNumbers: implementation, and the current #sum: implementation would be moved to #domainSum: (or some other appropriate name...)? As for #detectSum: I think this method has confusing name and is functionally duplicate to #sum:. The swapped arguments there may make sense when you overload #+, however the same thing can be achieved from the provided block ... On 12/01, Max Leske wrote:
Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
Can we settle for a single implementation? Such as (modified from #sumNumbers:):
newSum: aBlock ^ self inject: (self ifEmpty: [ 0 ] ifNotEmpty: [ self anyOne ]) into: [ :sum :each | sum + (aBlock value: each) ]
This implementation combines the best of the three implementations I think. Benchmark:
[ 100 timesRepeat: [ (1 to: 1000000) newSum: #yourself ] ] timeToRun 17955
BTW, there is also the message #sum, which suffers from the same problem as #sum: (the implementation is basically a copy). #sum could be implemented as
sum ^ self sum: [ :x | x ]
As for the name for the new method, I suggest #sum:.
Cheers, Max
-- Peter
Hmm. On Tue, Dec 1, 2015 at 12:17 AM, Max Leske <maxleske@gmail.com> wrote:
Hi guys,
Collection defines #sum:, #detectSum: and #sumNumbers:, all of which accomplish the same (in principal) but with subtle differences:
#sum: - uses #inject:into: with #anyOne as the injected element and will thus fail for empty collections
#detectSum: - uses ânextValue + sumâ instead of âsum + nextValueâ which makes it a lot slower when dealing with large numbers (primitive fails and number conversion is necessary)
Interesting. On my machine (which is apparently really slow right now), I get: [ 100 timesRepeat: [ (1 to: 1000000) detectSum: [:x|x] ] ] timeToRun 104874 [ 100 timesRepeat: [ (1 to: 1000000) detectSumSwitch: [:x|x] ] ] timeToRun 44369 (this second one is where I add have switched the order - sum + nextValue - for speed. So, a little over twice as fast).
If I change SmallInteger to be a bit smarter, I get: [ 100 timesRepeat: [ (1 to: 1000000) detectSum: [:x|x] ] ] timeToRun 45257 In other words just marginally slower than the optimized method above. The benefit here is that future folks won't have to remember which way to do their arithmetic to make things fast. Smarter + (in SmallInteger): + aNumber "Primitive. Add the receiver to the argument and answer with the result if it is a SmallInteger. Fail if the argument or the result is not a SmallInteger Essential No Lookup. See Object documentation whatIsAPrimitive." <primitive: 1> ^aNumber class = SmallInteger ifFalse: [aNumber + self] ifTrue: [super + aNumber] Of course, this wants to be thoroughly tested before use - certain additions may now work in ways not expected before (such as adding an array to a small integer - you may get odd results). Still...
#sumNumbers: - same as #sum but doesnât fail for empty collections. Only good for numbers though.
Benchmarks:
[ 100 timesRepeat: [ (1 to: 1000000) sum: #yourself ] ] timeToRun 18062 [ 100 timesRepeat: [ (1 to: 1000000) detectSum: #yourself ] ] timeToRun 42391 [ 100 timesRepeat: [ (1 to: 1000000) sumNumbers: #yourself ] ] timeToRun 18096
<snip>
-cbc
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes. [[[ aCollection arithmetic sum: [...] or.... aCollection arithmetic avg. ]]]
I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue. I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods: sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ] sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ] sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty: In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum). Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case. Please cast your vote for these changes: 1. Do you agree to changing #sum and #sum: in the suggested way? 2. Do you agree to the removal of #detectSum:? 3. Do you agree to the removal of #sumNumbers? 4. Do you agree that the #max and #min selectors also need to be adapted? Thanks for you help. Cheers, Max Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.) Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB) Benchmarks of the current versions: [ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second [ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second [ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second [ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second Benchmarks of the new versions: [ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second [ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second [ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second [ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second [ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second [ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
Max, sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block. sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote:
I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com <mailto:maxleske@gmail.com>> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr <mailto:stepharo@free.fr>> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
Hi, Could we not have sum, but sumNumbers instead? So, we would end up with: sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com "There are no old things, there are only old ways of looking at them."
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I could live with that. We would sacrifice the beautiful selector #sum for the more intention revealing #sumNumbers. I think that makes sense. Concerning the #min and #max methods that means weâd end up with e.g. #minNumbers, #min: and #min:ifEmpty: etc.
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
Hi,
On Dec 20, 2015, at 2:30 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I could live with that. We would sacrifice the beautiful selector #sum for the more intention revealing #sumNumbers. I think that makes sense.
Concerning the #min and #max methods that means weâd end up with e.g. #minNumbers, #min: and #min:ifEmpty: etc.
Yes. Doru
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
-- www.tudorgirba.com www.feenk.com "If you can't say why something is relevant, it probably isn't."
NO
On 20 Dec 2015, at 14:32, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 20, 2015, at 2:30 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I could live with that. We would sacrifice the beautiful selector #sum for the more intention revealing #sumNumbers. I think that makes sense.
Concerning the #min and #max methods that means weâd end up with e.g. #minNumbers, #min: and #min:ifEmpty: etc.
Yes.
Doru
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
-- www.tudorgirba.com www.feenk.com
"If you can't say why something is relevant, it probably isn't."
Hi, I am not sure I understand the meaning of NO in the context of the previous message. Is the NO related to the sumNumbers, or to the min, max? Cheers, Doru
On Dec 20, 2015, at 2:43 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
NO
On 20 Dec 2015, at 14:32, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 20, 2015, at 2:30 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I could live with that. We would sacrifice the beautiful selector #sum for the more intention revealing #sumNumbers. I think that makes sense.
Concerning the #min and #max methods that means weâd end up with e.g. #minNumbers, #min: and #min:ifEmpty: etc.
Yes.
Doru
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
-- www.tudorgirba.com www.feenk.com
"If you can't say why something is relevant, it probably isn't."
-- www.tudorgirba.com www.feenk.com "Beauty is where we see it."
No means I don't want to start adding Numbers to beautiful unary selectors. I don't understand why you don't understand that point about beauty and simplicity.
On 20 Dec 2015, at 14:54, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
I am not sure I understand the meaning of NO in the context of the previous message.
Is the NO related to the sumNumbers, or to the min, max?
Cheers, Doru
On Dec 20, 2015, at 2:43 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
NO
On 20 Dec 2015, at 14:32, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
On Dec 20, 2015, at 2:30 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I could live with that. We would sacrifice the beautiful selector #sum for the more intention revealing #sumNumbers. I think that makes sense.
Concerning the #min and #max methods that means weâd end up with e.g. #minNumbers, #min: and #min:ifEmpty: etc.
Yes.
Doru
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
-- www.tudorgirba.com www.feenk.com
"If you can't say why something is relevant, it probably isn't."
-- www.tudorgirba.com www.feenk.com
"Beauty is where we see it."
Doru, For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics). Max's point is to make everything more consistent and remove some API. I support that too. Now, I like Max's proposal. But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear. Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
Hi, Itâs clear that choices we would make would not be the same :). So, letâs drop past discussions and stay with the current situation. Do you agree with the observation that in the proposal of Max #sum and #sum: would not share the same meaning? If yes, do you agree that it would be misleading from the outside? I would be fine with either answer. I would just want to understand. Cheers, Doru
On Dec 20, 2015, at 2:43 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
-- www.tudorgirba.com www.feenk.com "It's not how it is, it is how we see it."
On 20 Dec 2015, at 14:57, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Itâs clear that choices we would make would not be the same :). So, letâs drop past discussions and stay with the current situation. Do you agree with the observation that in the proposal of Max #sum and #sum: would not share the same meaning? If yes, do you agree that it would be misleading from the outside?
The overal meaning is the same, IMO, summing a number of things (most often numbers) using #+. The processing block just adds functionality. The behaviour in the case of an empty collection is different yes. I want 0, why, because of https://en.wikipedia.org/wiki/Empty_sum This is about optimisation for the common case. VW had no #sum, I think because of all the points raised in this discussion (you can't always expect numbers and it is not clear what to do with empty collections). Check out the senders of #sum and #sum: in a 50 image.
I would be fine with either answer. I would just want to understand.
Cheers, Doru
On Dec 20, 2015, at 2:43 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
-- www.tudorgirba.com www.feenk.com
"It's not how it is, it is how we see it."
Hi,
On Dec 20, 2015, at 3:07 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 20 Dec 2015, at 14:57, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Itâs clear that choices we would make would not be the same :). So, letâs drop past discussions and stay with the current situation. Do you agree with the observation that in the proposal of Max #sum and #sum: would not share the same meaning? If yes, do you agree that it would be misleading from the outside?
The overal meaning is the same, IMO, summing a number of things (most often numbers) using #+. The processing block just adds functionality.
The behaviour in the case of an empty collection is different yes. I want 0, why, because of https://en.wikipedia.org/wiki/Empty_sum
This is about optimisation for the common case.
Fair enough. But, then why are you not equally picking on the fact that #sum: throws an error? :) We are really overusing a term for something we should not and I think in the process we are getting to a tradeoff that does not lead to simplicity. From my point of view this discussion is similar to having Object>>#name. It kind of makes common sense to ask an object for its name, only that in a few situations it is actually painful to have it there :)
VW had no #sum, I think because of all the points raised in this discussion (you can't always expect numbers and it is not clear what to do with empty collections).
I would not guide our decisions based on what VW did or did not do (I could not resist :)).
Check out the senders of #sum and #sum: in a 50 image.
Sure, but we are supposed to create a language that is used by others. In the base image, there is no problem with Object>>#name either, but it is still not a good thing :) So, why not remove #sum altogether then? Cheers, Doru
I would be fine with either answer. I would just want to understand.
Cheers, Doru
On Dec 20, 2015, at 2:43 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
2. Do you agree to the removal of #detectSum:?
3. Do you agree to the removal of #sumNumbers?
4. Do you agree that the #max and #min selectors also need to be adapted?
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- www.tudorgirba.com www.feenk.com
"There are no old things, there are only old ways of looking at them."
-- www.tudorgirba.com www.feenk.com
"It's not how it is, it is how we see it."
-- www.tudorgirba.com www.feenk.com "What we can governs what we wish."
On Mon, Dec 21, 2015 at 12:43 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I agree somewhat with Duro to avoid #sum and #sum: having different semantics, but I agree more with Sven about keeping #sum for numbers only and returning zero, so why not turn Doru's suggestion around... #sum (empty --> 0) #sumBy: (empty --> error) #sumBy: ifEmpty: (empty --> no error) alternatively could be #sumUsing: or #sumWith: or something similar.
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes
2. Do you agree to the removal of #detectSum:? ?? (not familiar)
3. Do you agree to the removal of #sumNumbers? yes
4. Do you agree that the #max and #min selectors also need to be adapted? ?? (not familiar)
On 20 Dec 2015, at 15:26, Ben Coman <btc@openInWorld.com> wrote:
On Mon, Dec 21, 2015 at 12:43 AM, Sven Van Caekenberghe <sven@stfx.eu <mailto:sven@stfx.eu>> wrote:
Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I agree somewhat with Duro to avoid #sum and #sum: having different semantics, but I agree more with Sven about keeping #sum for numbers only and returning zero, so why not turn Doru's suggestion around...
#sum (empty --> 0) #sumBy: (empty --> error) #sumBy: ifEmpty: (empty --> no error)
alternatively could be #sumUsing: or #sumWith: or something similar.
Also a good idea. However, looking at the methods on Collection, the pattern is usually #message and #message:, e.g. #sorted and #sorted:. There used to be #sortBy: but it was removed because the view was that the protocol semantics should be the same across all methods. The current case is a bit different of course since weâre dealing with the problem of the zero element which doesnât arise in most other cases. Still, I think from a users point of view it would be strange to have to use #sumBy: when every other message pair uses the #message / #message: pattern. I feel that this argument (thanks Ben :) ) is also a valid point against Doruâs argument for #sumNumbers. While #sumNumbers may *technically* be the best name (which we canât seem to agree onâ¦), #sum, #sum: and #sum:ifEmpty: form a triple that naturally fits into the naming protocol applied elsewhere. Cheers, Max
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes
2. Do you agree to the removal of #detectSum:? ?? (not familiar)
3. Do you agree to the removal of #sumNumbers? yes
4. Do you agree that the #max and #min selectors also need to be adapted? ?? (not familiar)
I did a pass on all the changes that would be required (whatever the outcome of this discussion). Looks easy enough. One interesting point: FloatArray>>sum is implemented as a primitive in the FloatArrayPlugin and the zero element is explicitly defined as 0.0. The primitive will not fail for an empty collection but return 0.0. That would be in line with our new definition of #sum. One other thing: should the old selectors be marked as deprecated rather than being removed? I think thatâs the general policy, right? Cheers, Max
On 20 Dec 2015, at 18:19, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 15:26, Ben Coman <btc@openInWorld.com <mailto:btc@openinworld.com>> wrote:
On Mon, Dec 21, 2015 at 12:43 AM, Sven Van Caekenberghe <sven@stfx.eu <mailto:sven@stfx.eu>> wrote:
Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com <mailto:tudor@tudorgirba.com>> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I agree somewhat with Duro to avoid #sum and #sum: having different semantics, but I agree more with Sven about keeping #sum for numbers only and returning zero, so why not turn Doru's suggestion around...
#sum (empty --> 0) #sumBy: (empty --> error) #sumBy: ifEmpty: (empty --> no error)
alternatively could be #sumUsing: or #sumWith: or something similar.
Also a good idea. However, looking at the methods on Collection, the pattern is usually #message and #message:, e.g. #sorted and #sorted:. There used to be #sortBy: but it was removed because the view was that the protocol semantics should be the same across all methods. The current case is a bit different of course since weâre dealing with the problem of the zero element which doesnât arise in most other cases. Still, I think from a users point of view it would be strange to have to use #sumBy: when every other message pair uses the #message / #message: pattern.
I feel that this argument (thanks Ben :) ) is also a valid point against Doruâs argument for #sumNumbers. While #sumNumbers may *technically* be the best name (which we canât seem to agree onâ¦), #sum, #sum: and #sum:ifEmpty: form a triple that naturally fits into the naming protocol applied elsewhere.
Cheers, Max
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com <mailto:maxleske@gmail.com>> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com <mailto:g.cotelli@gmail.com>> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com <mailto:maxleske@gmail.com>> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr <mailto:stepharo@free.fr>> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes
2. Do you agree to the removal of #detectSum:? ?? (not familiar)
3. Do you agree to the removal of #sumNumbers? yes
4. Do you agree that the #max and #min selectors also need to be adapted? ?? (not familiar)
Hi, I cannot seem to agree with the current line of reasoning, so I am withdrawing from this debate. Itâs late in the year and I know I need a break, so it is likely that I am missing something obvious or that I am just persisting in some sort of bike-shedding point of view :). Thanks for keeping up with this. I wish you all a lovely holiday and see you next year! Doru
On Dec 20, 2015, at 8:23 PM, Max Leske <maxleske@gmail.com> wrote:
I did a pass on all the changes that would be required (whatever the outcome of this discussion). Looks easy enough. One interesting point: FloatArray>>sum is implemented as a primitive in the FloatArrayPlugin and the zero element is explicitly defined as 0.0. The primitive will not fail for an empty collection but return 0.0. That would be in line with our new definition of #sum.
One other thing: should the old selectors be marked as deprecated rather than being removed? I think thatâs the general policy, right?
Cheers, Max
On 20 Dec 2015, at 18:19, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 15:26, Ben Coman <btc@openInWorld.com> wrote:
On Mon, Dec 21, 2015 at 12:43 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I agree somewhat with Duro to avoid #sum and #sum: having different semantics, but I agree more with Sven about keeping #sum for numbers only and returning zero, so why not turn Doru's suggestion around...
#sum (empty --> 0) #sumBy: (empty --> error) #sumBy: ifEmpty: (empty --> no error)
alternatively could be #sumUsing: or #sumWith: or something similar.
Also a good idea. However, looking at the methods on Collection, the pattern is usually #message and #message:, e.g. #sorted and #sorted:. There used to be #sortBy: but it was removed because the view was that the protocol semantics should be the same across all methods. The current case is a bit different of course since weâre dealing with the problem of the zero element which doesnât arise in most other cases. Still, I think from a users point of view it would be strange to have to use #sumBy: when every other message pair uses the #message / #message: pattern.
I feel that this argument (thanks Ben :) ) is also a valid point against Doruâs argument for #sumNumbers. While #sumNumbers may *technically* be the best name (which we canât seem to agree onâ¦), #sum, #sum: and #sum:ifEmpty: form a triple that naturally fits into the naming protocol applied elsewhere.
Cheers, Max
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes
2. Do you agree to the removal of #detectSum:? ?? (not familiar)
3. Do you agree to the removal of #sumNumbers? yes
4. Do you agree that the #max and #min selectors also need to be adapted? ?? (not familiar)
-- www.tudorgirba.com www.feenk.com "Speaking louder won't make the point worthier."
I would suggest not to change anything for now, let's wait for Max's poll or feedback from others. We could also pick this up again when we see each other IRL, like at the Pharo Days, that will certainly make it easier.
On 20 Dec 2015, at 22:09, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
I cannot seem to agree with the current line of reasoning, so I am withdrawing from this debate. Itâs late in the year and I know I need a break, so it is likely that I am missing something obvious or that I am just persisting in some sort of bike-shedding point of view :).
Thanks for keeping up with this.
I wish you all a lovely holiday and see you next year!
Doru
On Dec 20, 2015, at 8:23 PM, Max Leske <maxleske@gmail.com> wrote:
I did a pass on all the changes that would be required (whatever the outcome of this discussion). Looks easy enough. One interesting point: FloatArray>>sum is implemented as a primitive in the FloatArrayPlugin and the zero element is explicitly defined as 0.0. The primitive will not fail for an empty collection but return 0.0. That would be in line with our new definition of #sum.
One other thing: should the old selectors be marked as deprecated rather than being removed? I think thatâs the general policy, right?
Cheers, Max
On 20 Dec 2015, at 18:19, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 15:26, Ben Coman <btc@openInWorld.com> wrote:
On Mon, Dec 21, 2015 at 12:43 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
Sven
On 20 Dec 2015, at 14:10, Tudor Girba <tudor@tudorgirba.com> wrote:
Hi,
Could we not have sum, but sumNumbers instead? So, we would end up with:
sum:ifEmpty: sum: (with error) sumNumbers (without error)
From the outside, #sum: looks like it should parameterize #sum, but the implementation is actually different. So, given that in this implementation #sum is not a special case of #sum: the two should be named differently to reflect that difference. Hence my proposal is to keep #sumNumbers instead of #sum.
I agree somewhat with Duro to avoid #sum and #sum: having different semantics, but I agree more with Sven about keeping #sum for numbers only and returning zero, so why not turn Doru's suggestion around...
#sum (empty --> 0) #sumBy: (empty --> error) #sumBy: ifEmpty: (empty --> no error)
alternatively could be #sumUsing: or #sumWith: or something similar.
Also a good idea. However, looking at the methods on Collection, the pattern is usually #message and #message:, e.g. #sorted and #sorted:. There used to be #sortBy: but it was removed because the view was that the protocol semantics should be the same across all methods. The current case is a bit different of course since weâre dealing with the problem of the zero element which doesnât arise in most other cases. Still, I think from a users point of view it would be strange to have to use #sumBy: when every other message pair uses the #message / #message: pattern.
I feel that this argument (thanks Ben :) ) is also a valid point against Doruâs argument for #sumNumbers. While #sumNumbers may *technically* be the best name (which we canât seem to agree onâ¦), #sum, #sum: and #sum:ifEmpty: form a triple that naturally fits into the naming protocol applied elsewhere.
Cheers, Max
Cheers, Doru
On Dec 20, 2015, at 1:47 PM, Max Leske <maxleske@gmail.com> wrote:
On 20 Dec 2015, at 13:43, Gabriel Cotelli <g.cotelli@gmail.com> wrote:
Max,
sum: aBlock ifEmpty: emptyBlock needs to obtain the sample evaluating the block.
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := aBlock value: self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Thanks! Missed that.
On Sun, Dec 20, 2015 at 8:59 AM, Max Leske <maxleske@gmail.com> wrote: I would like to wrap up this discussion.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes
2. Do you agree to the removal of #detectSum:? ?? (not familiar)
3. Do you agree to the removal of #sumNumbers? yes
4. Do you agree that the #max and #min selectors also need to be adapted? ?? (not familiar)
-- www.tudorgirba.com www.feenk.com
"Speaking louder won't make the point worthier."
On Wed, Dec 2, 2015 at 3:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
I am saying that if you want sum: to be generic, it cannot assume a *specific* Zero object. And sum: should be generic because of its name.
This seems the crux of the disparate viewpoints, which is why I suggested return a *generic* Zero object as follows...
On 04 Dec 2015, at 01:49, Ben Coman <btc@openInWorld.com> wrote: do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
On Sat, Dec 5, 2015 at 3:12 AM, Max Leske <maxleske@gmail.com> wrote:
While I think you might be on to something, I think we should take small steps. Iâd be happy already if we can just get rid of one superfluous method and provide a better API without starting to think about the deeper semantics.
Sure, small steps are better, except when this hits a sticking point that the bigger step may overcome. I think returning a generic zero allowing the program to proceed is better than throwing an generic error that the programmer needs to explicitly deal with. The concept of zero is well defined for the common arithmetic functions. Just for a thought experiment, what arithmetic functions would need to be dealt with.
ArithmeticZero class >> + anObject ^anObject
AritmeticZero class >> * anObject ^ anObject zero AritmeticZero class >> - anObject ^ anObject zero - anObject
Also, I think thereâs a reason why Aconcagua is an external package: developers usually are happy to work with numbers (which are already objects in Smalltalk anyway) and theyâre fast.
Sure its an external package, but when you have a collection of weight readings for example, you want to use the internal convenient #sum method... { 4 kg . 5 kg . 6 kg } sum --> 16kg { } sum + 1 kg --> 1kg { } sum * 1 kg --> 0 kg^2 "hmmm, zero with units and multiplication starts getting more complicated - maybe should be left out of this discussion"
While #sumNumbers may *technically* be the best name (which we canât seem to agree onâ¦), #sum, #sum: and #sum:ifEmpty: form a triple that naturally fits into the naming protocol applied elsewhere.
So maybe alternative naming of the generic zero... #sum (empty --> EmptyAggregateZero) #sum: (empty --> EmptyAggregateZero) #sum: ifEmpty: (empty --> whatever)
On Mon, Dec 21, 2015 at 12:43 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote: Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
#sum is very convenient and that lowers the barrier of entry for newcomers over the relatively exotic #inject:into: We should keep trying to work through the sticky points, but maybe you guys getting together IRL is better than continuing on the mail list. cheers -ben
2015-12-22 3:46 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Wed, Dec 2, 2015 at 3:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
I am saying that if you want sum: to be generic, it cannot assume a *specific* Zero object. And sum: should be generic because of its name.
This seems the crux of the disparate viewpoints, which is why I suggested return a *generic* Zero object as follows...
On 04 Dec 2015, at 01:49, Ben Coman <btc@openInWorld.com> wrote: do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
Nice, but generic zero can't work: you sometimes need to decide if it is the zero of vector space, or of associated field. The generic zero can't behave as both say a matrix and a scalar...
On Sat, Dec 5, 2015 at 3:12 AM, Max Leske <maxleske@gmail.com> wrote:
While I think you might be on to something, I think we should take small steps. Iâd be happy already if we can just get rid of one superfluous method and provide a better API without starting to think about the deeper semantics.
Sure, small steps are better, except when this hits a sticking point that the bigger step may overcome. I think returning a generic zero allowing the program to proceed is better than throwing an generic error that the programmer needs to explicitly deal with. The concept of zero is well defined for the common arithmetic functions. Just for a thought experiment, what arithmetic functions would need to be dealt with.
ArithmeticZero class >> + anObject ^anObject
AritmeticZero class >> * anObject ^ anObject zero
AritmeticZero class >> - anObject ^ anObject zero - anObject
Also, I think thereâs a reason why Aconcagua is an external package: developers usually are happy to work with numbers (which are already objects in Smalltalk anyway) and theyâre fast.
Sure its an external package, but when you have a collection of weight readings for example, you want to use the internal convenient #sum method... { 4 kg . 5 kg . 6 kg } sum --> 16kg { } sum + 1 kg --> 1kg { } sum * 1 kg --> 0 kg^2 "hmmm, zero with units and multiplication starts getting more complicated - maybe should be left out of this discussion"
While #sumNumbers may *technically* be the best name (which we canât seem to agree onâ¦), #sum, #sum: and #sum:ifEmpty: form a triple that naturally fits into the naming protocol applied elsewhere.
So maybe alternative naming of the generic zero... #sum (empty --> EmptyAggregateZero) #sum: (empty --> EmptyAggregateZero) #sum: ifEmpty: (empty --> whatever)
On Mon, Dec 21, 2015 at 12:43 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote: Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
#sum is very convenient and that lowers the barrier of entry for newcomers over the relatively exotic #inject:into: We should keep trying to work through the sticky points, but maybe you guys getting together IRL is better than continuing on the mail list.
cheers -ben
On 22 Dec 2015, at 10:22, Nicolas Cellier <nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-22 3:46 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 3:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
I am saying that if you want sum: to be generic, it cannot assume a *specific* Zero object. And sum: should be generic because of its name.
This seems the crux of the disparate viewpoints, which is why I suggested return a *generic* Zero object as follows...
On 04 Dec 2015, at 01:49, Ben Coman <btc@openInWorld.com> wrote: do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
Nice, but generic zero can't work: you sometimes need to decide if it is the zero of vector space, or of associated field. The generic zero can't behave as both say a matrix and a scalar...
Indeed, that is what I said the first time Ben proposed this. But I am curious, Nicolas, what you think of this, with your math background/interest ?
On Sat, Dec 5, 2015 at 3:12 AM, Max Leske <maxleske@gmail.com> wrote:
While I think you might be on to something, I think we should take small steps. Iâd be happy already if we can just get rid of one superfluous method and provide a better API without starting to think about the deeper semantics.
Sure, small steps are better, except when this hits a sticking point that the bigger step may overcome. I think returning a generic zero allowing the program to proceed is better than throwing an generic error that the programmer needs to explicitly deal with. The concept of zero is well defined for the common arithmetic functions. Just for a thought experiment, what arithmetic functions would need to be dealt with.
ArithmeticZero class >> + anObject ^anObject
AritmeticZero class >> * anObject ^ anObject zero
AritmeticZero class >> - anObject ^ anObject zero - anObject
Also, I think thereâs a reason why Aconcagua is an external package: developers usually are happy to work with numbers (which are already objects in Smalltalk anyway) and theyâre fast.
Sure its an external package, but when you have a collection of weight readings for example, you want to use the internal convenient #sum method... { 4 kg . 5 kg . 6 kg } sum --> 16kg { } sum + 1 kg --> 1kg { } sum * 1 kg --> 0 kg^2 "hmmm, zero with units and multiplication starts getting more complicated - maybe should be left out of this discussion"
While #sumNumbers may *technically* be the best name (which we canât seem to agree onâ¦), #sum, #sum: and #sum:ifEmpty: form a triple that naturally fits into the naming protocol applied elsewhere.
So maybe alternative naming of the generic zero... #sum (empty --> EmptyAggregateZero) #sum: (empty --> EmptyAggregateZero) #sum: ifEmpty: (empty --> whatever)
On Mon, Dec 21, 2015 at 12:43 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote: Doru,
For me this whole discussion started because you (the standpoint that you take) hijacked the best selector (#sum) for a use case that is much less common than adding a collection of numbers which should give 0 when empty (you hijack it by not wanting to return 0, which I totally understand, but doing so you redefine the meaning/semantics).
Max's point is to make everything more consistent and remove some API. I support that too.
Now, I like Max's proposal.
But, you know, my conclusion when the thread ended was that it might be better to throw all these selectors away, since #inject:into: covers most use cases at least as well and at least as clear.
#sum is very convenient and that lowers the barrier of entry for newcomers over the relatively exotic #inject:into: We should keep trying to work through the sticky points, but maybe you guys getting together IRL is better than continuing on the mail list.
cheers -ben
2015-12-22 10:37 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
On 22 Dec 2015, at 10:22, Nicolas Cellier < nicolas.cellier.aka.nice@gmail.com> wrote:
2015-12-22 3:46 GMT+01:00 Ben Coman <btc@openinworld.com>: On Wed, Dec 2, 2015 at 3:38 AM, Tudor Girba <tudor@tudorgirba.com> wrote:
I am saying that if you want sum: to be generic, it cannot assume a *specific* Zero object. And sum: should be generic because of its name.
This seems the crux of the disparate viewpoints, which is why I suggested return a *generic* Zero object as follows...
On 04 Dec 2015, at 01:49, Ben Coman <btc@openInWorld.com> wrote: do something like...
Collection>>sum | sum sample | self isEmpty ifTrue: [ ^ ArithmeticZero ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + each ]. ^ sum - sample
Nice, but generic zero can't work: you sometimes need to decide if it is the zero of vector space, or of associated field. The generic zero can't behave as both say a matrix and a scalar...
Indeed, that is what I said the first time Ben proposed this.
But I am curious, Nicolas, what you think of this, with your math background/interest ?
This is what I had in old app (in the 90s): SequenceableCollection>>sum: aBlock "answer the sum of all elements after applying a Block" | sum | self isEmpty ifTrue: [^0]. sum := aBlock value: (self at: 1). 2 to: self size do: [:i | sum := sum + (aBlock value: (self at: i))]. ^sum Collection>>sum: aBlock "answer the sum of all elements after applying a Block" | sum | sum := 0. self do: [:e | sum := sum + (aBlock value: e)]. ^sum I find sum: much more expressive than inject:into: If we really find that it adds semantic value to inject:into: we may also implement optionnal: Collection>>sum: aBlock to: initialValue "answer the sum of all elements after applying a Block" | sum | sum := initialValue. self do: [:e | sum := sum + (aBlock value: e)]. ^sum Collection>>sum: aBlock ^self sum: aBlock to: 0 This way, no (#anyOne,#-) trick required, that's why I prefer sum:to: to sum:ifEmpty:... For example, I had symbolic algebra, and the #- trick would have required further simplification of the sum. So I don't have any mathematical solution, just pragmatic ones: - I agree on this: no need for many selectors, one or maximum two should be enough - I wouldn't care too much and would just answer 0 for empty collection because we have inject:into: or optionnally sum:to: for exotic cases
Just a remark. I think that we discarded the proposition of having aCol arithmetic sum but I found it nice because there if was clear that you want to get back 0 for #(). Stef
Just a remark. I think that we discarded the proposition of having aCol arithmetic sum but I found it nice because there if was clear that you want to get back 0 for #(). Stef
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt Best regards, Henrik -----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers: Just a remark. I think that we discarded the proposition of having aCol arithmetic sum but I found it nice because there if was clear that you want to get back 0 for #(). Stef
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives. This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result: There are some examples in the class comments. Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
I like these last. Particularly because - it cleans the collectionâs API - we can continue extending this idea to add parallelism, mutual exclusion...
On 29 dic 2015, at 11:53 p.m., Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
There are some examples in the class comments.
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
On Mon, Jan 4, 2016 at 10:55 PM, Guillermo Polito <guillermopolito@gmail.com> wrote:
I like these last.
Particularly because
- it cleans the collectionâs API - we can continue extending this idea to add parallelism, mutual exclusion...
I don't understand the second point.
On 29 dic 2015, at 11:53 p.m., Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
Interesting to great enthusiasm from several people. Looks like I'm in the minority but I'm wary of this. It seems like over-intellectualized design. I'll need to remember whether to send #magnitude or #numbers before an operation and which matches up with which operations. This seems harder for newcomers. Are there any other programming languages that do it this way?
There are some examples in the class comments.
Class comment says CollectionOperations is "a class that offers extended API to operate on collections assumed to contain objects of a certain type."
1. CollectionOperations 2. OperationsOnMagnitudes 3. OperationsOnNumbers 4. OperationsOnSequenceableNumbers
So 2 & 3 refer to the elements of the collection, so 4 makes me wonder what elements are sequence-able numbers? So 4 breaks the pattern to refer to the type of collection not just the type of element. Why is #average defined for OperationsOnNumbers rather than OperationsOnMagnitudes? If I have a collection of magnitudes like Time "17:28 . 17:29 . 17:31 . 17:32" or Duration "4 minutes . 6 minutes" I would expect to be able to get 17:30 and 5 minutes as the respective averages. But then it doesn't make sense to average other magnitudes like Character, so where does that leave us? Also I'd like to sum Durations but its not defined for OperationsOnMagnitudes. I guess I fear there is hidden complexity for little gain. Maybe those that like the idea can collaborate on a package they use on their own projects to work out the kinks. cheers -ben
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
On 07 Jan 2016, at 14:23, Ben Coman <btc@openinworld.com> wrote:
On Mon, Jan 4, 2016 at 10:55 PM, Guillermo Polito <guillermopolito@gmail.com> wrote:
I like these last.
Particularly because
- it cleans the collectionâs API - we can continue extending this idea to add parallelism, mutual exclusion...
I don't understand the second point.
Wrapping an object in another to add behaviour is a standard technique (as opposed to adding the behaviour to the object itself). There can be several reasons for doing this. It is not necessarily or always a good idea either (for example, extension protocols are nice too).
On 29 dic 2015, at 11:53 p.m., Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
Interesting to great enthusiasm from several people. Looks like I'm in the minority but I'm wary of this. It seems like over-intellectualized design.
Yes, maybe ;-)
I'll need to remember whether to send #magnitude or #numbers before an operation and which matches up with which operations. This seems harder for newcomers.
#(1 2 foo true false) sum => ? #() sum => ? { window1. window2. window3 } max => ?
Are there any other programming languages that do it this way?
There are not so many fully dynamic languages with non-homogeneous collections. Most languages have static typing and generic types (both are not what we want). But I would love to see how comparable languages solve this 'problem'. Most language won't allow (or don't define) operations on collections that could fail because of the fact that they contain the wrong objects. Having said that, I am not saying that I want to move away from our flexibility/simplicity !
There are some examples in the class comments.
Class comment says CollectionOperations is "a class that offers extended API to operate on collections assumed to contain objects of a certain type."
1. CollectionOperations 2. OperationsOnMagnitudes 3. OperationsOnNumbers 4. OperationsOnSequenceableNumbers
So 2 & 3 refer to the elements of the collection, so 4 makes me wonder what elements are sequence-able numbers? So 4 breaks the pattern to refer to the type of collection not just the type of element.
Yes, that is a problem, it feels wrong. There are several dimensions that cannot be captured in one simple hierarchy.
Why is #average defined for OperationsOnNumbers rather than OperationsOnMagnitudes? If I have a collection of magnitudes like Time "17:28 . 17:29 . 17:31 . 17:32" or Duration "4 minutes . 6 minutes" I would expect to be able to get 17:30 and 5 minutes as the respective averages. But then it doesn't make sense to average other magnitudes like Character, so where does that leave us?
You are wrong, a Magnitude is only comparable, it does not mean it can do arithmetic. You need arithmetic to compute average. In this case, you could use #numbers to indicate that you see your objects as compatible with that (and the time objects mostly are). But summing an empty collection of Durations, you would probably want Duration zero to be the result, and then we are back to our original problem ;-)
Also I'd like to sum Durations but its not defined for OperationsOnMagnitudes.
I guess I fear there is hidden complexity for little gain. Maybe those that like the idea can collaborate on a package they use on their own projects to work out the kinks.
cheers -ben
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
On Thu, Jan 7, 2016 at 9:40 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 07 Jan 2016, at 14:23, Ben Coman <btc@openinworld.com> wrote:
On Mon, Jan 4, 2016 at 10:55 PM, Guillermo Polito <guillermopolito@gmail.com> wrote:
I like these last.
Particularly because
- it cleans the collectionâs API - we can continue extending this idea to add parallelism, mutual exclusion...
I don't understand the second point.
Wrapping an object in another to add behaviour is a standard technique (as opposed to adding the behaviour to the object itself). There can be several reasons for doing this. It is not necessarily or always a good idea either (for example, extension protocols are nice too).
"standard technique"
Does it have a pattern name?, that I could look up to learn more (btw I have Design Patterns Smalltalk Companion - but its hard to remember them without using them in practice.) ? cheers -ben
On 29 dic 2015, at 11:53 p.m., Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
Interesting to great enthusiasm from several people. Looks like I'm in the minority but I'm wary of this. It seems like over-intellectualized design.
Yes, maybe ;-)
I'll need to remember whether to send #magnitude or #numbers before an operation and which matches up with which operations. This seems harder for newcomers.
#(1 2 foo true false) sum => ? #() sum => ? { window1. window2. window3 } max => ?
Are there any other programming languages that do it this way?
There are not so many fully dynamic languages with non-homogeneous collections. Most languages have static typing and generic types (both are not what we want). But I would love to see how comparable languages solve this 'problem'.
Most language won't allow (or don't define) operations on collections that could fail because of the fact that they contain the wrong objects.
Having said that, I am not saying that I want to move away from our flexibility/simplicity !
There are some examples in the class comments.
Class comment says CollectionOperations is "a class that offers extended API to operate on collections assumed to contain objects of a certain type."
1. CollectionOperations 2. OperationsOnMagnitudes 3. OperationsOnNumbers 4. OperationsOnSequenceableNumbers
So 2 & 3 refer to the elements of the collection, so 4 makes me wonder what elements are sequence-able numbers? So 4 breaks the pattern to refer to the type of collection not just the type of element.
Yes, that is a problem, it feels wrong. There are several dimensions that cannot be captured in one simple hierarchy.
Why is #average defined for OperationsOnNumbers rather than OperationsOnMagnitudes? If I have a collection of magnitudes like Time "17:28 . 17:29 . 17:31 . 17:32" or Duration "4 minutes . 6 minutes" I would expect to be able to get 17:30 and 5 minutes as the respective averages. But then it doesn't make sense to average other magnitudes like Character, so where does that leave us?
You are wrong, a Magnitude is only comparable, it does not mean it can do arithmetic. You need arithmetic to compute average.
In this case, you could use #numbers to indicate that you see your objects as compatible with that (and the time objects mostly are).
But summing an empty collection of Durations, you would probably want Duration zero to be the result, and then we are back to our original problem ;-)
Also I'd like to sum Durations but its not defined for OperationsOnMagnitudes.
I guess I fear there is hidden complexity for little gain. Maybe those that like the idea can collaborate on a package they use on their own projects to work out the kinks.
cheers -ben
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
2016-01-07 16:31 GMT+01:00 Ben Coman <btc@openinworld.com>:
On Thu, Jan 7, 2016 at 9:40 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 07 Jan 2016, at 14:23, Ben Coman <btc@openinworld.com> wrote:
On Mon, Jan 4, 2016 at 10:55 PM, Guillermo Polito <guillermopolito@gmail.com> wrote:
I like these last.
Particularly because
- it cleans the collectionâs API - we can continue extending this idea to add parallelism, mutual
exclusion...
I don't understand the second point.
Wrapping an object in another to add behaviour is a standard technique (as opposed to adding the behaviour to the object itself). There can be several reasons for doing this. It is not necessarily or always a good idea either (for example, extension protocols are nice too).
"standard technique"
Does it have a pattern name?, that I could look up to learn more (btw I have Design Patterns Smalltalk Companion - but its hard to remember them without using them in practice.) ?
Decorator? https://en.wikipedia.org/wiki/Decorator_pattern
cheers -ben
On 29 dic 2015, at 11:53 p.m., Sven Van Caekenberghe <sven@stfx.eu>
wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no>
wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
Interesting to great enthusiasm from several people. Looks like I'm in the minority but I'm wary of this. It seems like over-intellectualized design.
Yes, maybe ;-)
I'll need to remember whether to send #magnitude or #numbers before an operation and which matches up with which operations. This seems harder for newcomers.
#(1 2 foo true false) sum => ? #() sum => ? { window1. window2. window3 } max => ?
Are there any other programming languages that do it this way?
There are not so many fully dynamic languages with non-homogeneous collections. Most languages have static typing and generic types (both are not what we want). But I would love to see how comparable languages solve this 'problem'.
Most language won't allow (or don't define) operations on collections that could fail because of the fact that they contain the wrong objects.
Having said that, I am not saying that I want to move away from our flexibility/simplicity !
There are some examples in the class comments.
Class comment says CollectionOperations is "a class that offers extended API to operate on collections assumed to contain objects of a certain type."
1. CollectionOperations 2. OperationsOnMagnitudes 3. OperationsOnNumbers 4. OperationsOnSequenceableNumbers
So 2 & 3 refer to the elements of the collection, so 4 makes me wonder what elements are sequence-able numbers? So 4 breaks the pattern to refer to the type of collection not just the type of element.
Yes, that is a problem, it feels wrong. There are several dimensions that cannot be captured in one simple hierarchy.
Why is #average defined for OperationsOnNumbers rather than OperationsOnMagnitudes? If I have a collection of magnitudes like Time "17:28 . 17:29 . 17:31 . 17:32" or Duration "4 minutes . 6 minutes" I would expect to be able to get 17:30 and 5 minutes as the respective averages. But then it doesn't make sense to average other magnitudes like Character, so where does that leave us?
You are wrong, a Magnitude is only comparable, it does not mean it can do arithmetic. You need arithmetic to compute average.
In this case, you could use #numbers to indicate that you see your objects as compatible with that (and the time objects mostly are).
But summing an empty collection of Durations, you would probably want Duration zero to be the result, and then we are back to our original problem ;-)
Also I'd like to sum Durations but its not defined for OperationsOnMagnitudes.
I guess I fear there is hidden complexity for little gain. Maybe those that like the idea can collaborate on a package they use on their own projects to work out the kinks.
cheers -ben
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On
Behalf Of stepharo
Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
On 07 Jan 2016, at 16:31, Ben Coman <btc@openinworld.com> wrote:
On Thu, Jan 7, 2016 at 9:40 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 07 Jan 2016, at 14:23, Ben Coman <btc@openinworld.com> wrote:
On Mon, Jan 4, 2016 at 10:55 PM, Guillermo Polito <guillermopolito@gmail.com> wrote:
I like these last.
Particularly because
- it cleans the collectionâs API - we can continue extending this idea to add parallelism, mutual exclusion...
I don't understand the second point.
Wrapping an object in another to add behaviour is a standard technique (as opposed to adding the behaviour to the object itself). There can be several reasons for doing this. It is not necessarily or always a good idea either (for example, extension protocols are nice too).
"standard technique"
Does it have a pattern name?, that I could look up to learn more (btw I have Design Patterns Smalltalk Companion - but its hard to remember them without using them in practice.) ?
Something in the general direction of Proxy, Decorator and/or Adaptor ?
cheers -ben
On 29 dic 2015, at 11:53 p.m., Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
Interesting to great enthusiasm from several people. Looks like I'm in the minority but I'm wary of this. It seems like over-intellectualized design.
Yes, maybe ;-)
I'll need to remember whether to send #magnitude or #numbers before an operation and which matches up with which operations. This seems harder for newcomers.
#(1 2 foo true false) sum => ? #() sum => ? { window1. window2. window3 } max => ?
Are there any other programming languages that do it this way?
There are not so many fully dynamic languages with non-homogeneous collections. Most languages have static typing and generic types (both are not what we want). But I would love to see how comparable languages solve this 'problem'.
Most language won't allow (or don't define) operations on collections that could fail because of the fact that they contain the wrong objects.
Having said that, I am not saying that I want to move away from our flexibility/simplicity !
There are some examples in the class comments.
Class comment says CollectionOperations is "a class that offers extended API to operate on collections assumed to contain objects of a certain type."
1. CollectionOperations 2. OperationsOnMagnitudes 3. OperationsOnNumbers 4. OperationsOnSequenceableNumbers
So 2 & 3 refer to the elements of the collection, so 4 makes me wonder what elements are sequence-able numbers? So 4 breaks the pattern to refer to the type of collection not just the type of element.
Yes, that is a problem, it feels wrong. There are several dimensions that cannot be captured in one simple hierarchy.
Why is #average defined for OperationsOnNumbers rather than OperationsOnMagnitudes? If I have a collection of magnitudes like Time "17:28 . 17:29 . 17:31 . 17:32" or Duration "4 minutes . 6 minutes" I would expect to be able to get 17:30 and 5 minutes as the respective averages. But then it doesn't make sense to average other magnitudes like Character, so where does that leave us?
You are wrong, a Magnitude is only comparable, it does not mean it can do arithmetic. You need arithmetic to compute average.
In this case, you could use #numbers to indicate that you see your objects as compatible with that (and the time objects mostly are).
But summing an empty collection of Durations, you would probably want Duration zero to be the result, and then we are back to our original problem ;-)
Also I'd like to sum Durations but its not defined for OperationsOnMagnitudes.
I guess I fear there is hidden complexity for little gain. Maybe those that like the idea can collaborate on a package they use on their own projects to work out the kinks.
cheers -ben
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
I'd say that it is not quite a proxy nor a decorator as their main characteristic is to maintain the api of its wrapee, and so be polymorphic and transparent. At least not the number example. The main reason of this wrappers are to augment the api. But i do not know if that has a name. However, a wrapper implementing mutual exclusion could enter in the decorator category... Le 7 janv. 2016 16:38, "Sven Van Caekenberghe" <sven@stfx.eu> a écrit :
On 07 Jan 2016, at 16:31, Ben Coman <btc@openinworld.com> wrote:
On Thu, Jan 7, 2016 at 9:40 PM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 07 Jan 2016, at 14:23, Ben Coman <btc@openinworld.com> wrote:
On Mon, Jan 4, 2016 at 10:55 PM, Guillermo Polito <guillermopolito@gmail.com> wrote:
I like these last.
Particularly because
- it cleans the collectionâs API - we can continue extending this idea to add parallelism, mutual
exclusion...
I don't understand the second point.
Wrapping an object in another to add behaviour is a standard technique (as opposed to adding the behaviour to the object itself). There can be several reasons for doing this. It is not necessarily or always a good idea either (for example, extension protocols are nice too).
"standard technique"
Does it have a pattern name?, that I could look up to learn more (btw I have Design Patterns Smalltalk Companion - but its hard to remember them without using them in practice.) ?
Something in the general direction of Proxy, Decorator and/or Adaptor ?
cheers -ben
On 29 dic 2015, at 11:53 p.m., Sven Van Caekenberghe <sven@stfx.eu>
wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no>
wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
Interesting to great enthusiasm from several people. Looks like I'm in the minority but I'm wary of this. It seems like over-intellectualized design.
Yes, maybe ;-)
I'll need to remember whether to send #magnitude or #numbers before an operation and which matches up with which operations. This seems harder for newcomers.
#(1 2 foo true false) sum => ? #() sum => ? { window1. window2. window3 } max => ?
Are there any other programming languages that do it this way?
There are not so many fully dynamic languages with non-homogeneous collections. Most languages have static typing and generic types (both are not what we want). But I would love to see how comparable languages solve this 'problem'.
Most language won't allow (or don't define) operations on collections that could fail because of the fact that they contain the wrong objects.
Having said that, I am not saying that I want to move away from our flexibility/simplicity !
There are some examples in the class comments.
Class comment says CollectionOperations is "a class that offers extended API to operate on collections assumed to contain objects of a certain type."
1. CollectionOperations 2. OperationsOnMagnitudes 3. OperationsOnNumbers 4. OperationsOnSequenceableNumbers
So 2 & 3 refer to the elements of the collection, so 4 makes me wonder what elements are sequence-able numbers? So 4 breaks the pattern to refer to the type of collection not just the type of element.
Yes, that is a problem, it feels wrong. There are several dimensions that cannot be captured in one simple hierarchy.
Why is #average defined for OperationsOnNumbers rather than OperationsOnMagnitudes? If I have a collection of magnitudes like Time "17:28 . 17:29 . 17:31 . 17:32" or Duration "4 minutes . 6 minutes" I would expect to be able to get 17:30 and 5 minutes as the respective averages. But then it doesn't make sense to average other magnitudes like Character, so where does that leave us?
You are wrong, a Magnitude is only comparable, it does not mean it can do arithmetic. You need arithmetic to compute average.
In this case, you could use #numbers to indicate that you see your objects as compatible with that (and the time objects mostly are).
But summing an empty collection of Durations, you would probably want Duration zero to be the result, and then we are back to our original problem ;-)
Also I'd like to sum Durations but its not defined for OperationsOnMagnitudes.
I guess I fear there is hidden complexity for little gain. Maybe those that like the idea can collaborate on a package they use on their own projects to work out the kinks.
cheers -ben
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On
Behalf Of stepharo
Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
Thank you, Sven! I like this very much. This would also mean that we could deprecate Collection>>#sum altogether :)) Cheers, Doru
On Dec 30, 2015, at 12:53 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
There are some examples in the class comments.
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
-- www.tudorgirba.com www.feenk.com "Innovation comes in the least expected form. That is, if it is expected, it already happened."
Very, very cool Sven (and Henrik!)! And it even works with exotic objects: { 1@2. 2@3 } numbers sum ââ> (3@5)â Could Collections-Arithmetic be integrated into the collections operations? Not really sure thatâs a good idea, I just want to know. Cheers, Max
On 04 Jan 2016, at 16:19, Tudor Girba <tudor@tudorgirba.com> wrote:
Thank you, Sven!
I like this very much.
This would also mean that we could deprecate Collection>>#sum altogether :))
Cheers, Doru
On Dec 30, 2015, at 12:53 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
There are some examples in the class comments.
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
-- www.tudorgirba.com www.feenk.com
"Innovation comes in the least expected form. That is, if it is expected, it already happened."
Thanks for the (positive) feedback. My problem is: if you think this through, lots of things could change. Consider that I added #magnitudes this would imply that sorting should move too, because you cannot assume the objects to be sortable (i.e. Magnitudes or conforming to that protocol). Also, this would create a kind of parallel hierarchy, with more type information. It feels like a simple single inheritance hierarchy cannot cover all combinations (for example, being sequenceable and being numbers, as opposed to being just a collection of numbers). Sorting an unordered collection is also different from sorting an ordered one (this last one could keep its type, or even be sorted in place). So maybe this is a can of worms. I am all for dynamic typing, not for trying to add some (weak) for of static typing.
On 07 Jan 2016, at 13:14, Max Leske <maxleske@gmail.com> wrote:
Very, very cool Sven (and Henrik!)!
And it even works with exotic objects: { 1@2. 2@3 } numbers sum ââ> (3@5)â
Could Collections-Arithmetic be integrated into the collections operations? Not really sure thatâs a good idea, I just want to know.
Cheers, Max
On 04 Jan 2016, at 16:19, Tudor Girba <tudor@tudorgirba.com> wrote:
Thank you, Sven!
I like this very much.
This would also mean that we could deprecate Collection>>#sum altogether :))
Cheers, Doru
On Dec 30, 2015, at 12:53 AM, Sven Van Caekenberghe <sven@stfx.eu> wrote:
Hi Henrik,
On 25 Dec 2015, at 14:08, Henrik Nergaard <henrin10@student.uia.no> wrote:
Like this? http://smalltalkhub.com/#!/~Latsabben/NumIt
That is a cool take on a possible approach. Thanks for doing it, it makes it much easier to think about and discuss alternatives.
This inspired me to do something similar, but not quite. I am just thinking out loud by implementation. Here is my result:
<Collections-Operations-SvenVanCaekenberghe.1.mcz>
There are some examples in the class comments.
Sven
Best regards, Henrik
-----Original Message----- From: Pharo-dev [mailto:pharo-dev-bounces@lists.pharo.org] On Behalf Of stepharo Sent: Thursday, December 24, 2015 9:58 AM To: Pharo Development List <pharo-dev@lists.pharo.org> Subject: Re: [Pharo-dev] #sum:, #detectSum:, #sumNumbers:
Just a remark. I think that we discarded the proposition of having
aCol arithmetic sum
but I found it nice because there if was clear that you want to get back 0 for #().
Stef
-- www.tudorgirba.com www.feenk.com
"Innovation comes in the least expected form. That is, if it is expected, it already happened."
Sven may be should do the following: - introduce the best solution (not using your and henrik decomposition) - continue to think about it. I would like to see if Xtreams does not go in the same direction too. But I do not have the code at hand to think about it. Stef
On 08 Jan 2016, at 21:15, stepharo <stepharo@free.fr> wrote:
Sven
may be should do the following: - introduce the best solution (not using your and henrik decomposition) - continue to think about it.
There is no intermediary solution, we'll continue to think about it.
I would like to see if Xtreams does not go in the same direction too. But I do not have the code at hand to think about it.
Xtreams does not solve the problem either, I think. The key question is: can you write operations on objects, generic containers of element objects, that depend on the fact that the element objects are of a certain type (or protocol), but that might fail and make completely no sense for others. Consider the 'data get/put' category on PositionalStream with operations like #int32 and #int32: - these assume that this are byte streams. But in my book, streams can be anything, colors, records, points, fonts, ...
Stef
Ok. So while the âdecoratorâ approach looks fantastic it will obviously have its own drawbacks. Discussing this on the mailing list is very tiering, so I suggest that this should be discussed during the next sprint or Pharo days. Since I will not be able to attend either one, can I ask one of you to take responsibility for this? Sven or Doru maybe? Cheers, Max
On 11 Jan 2016, at 08:10, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 08 Jan 2016, at 21:15, stepharo <stepharo@free.fr> wrote:
Sven
may be should do the following: - introduce the best solution (not using your and henrik decomposition) - continue to think about it.
There is no intermediary solution, we'll continue to think about it.
I would like to see if Xtreams does not go in the same direction too. But I do not have the code at hand to think about it.
Xtreams does not solve the problem either, I think.
The key question is: can you write operations on objects, generic containers of element objects, that depend on the fact that the element objects are of a certain type (or protocol), but that might fail and make completely no sense for others.
Consider the 'data get/put' category on PositionalStream with operations like #int32 and #int32: - these assume that this are byte streams. But in my book, streams can be anything, colors, records, points, fonts, ...
Stef
2016-01-07 13:14 GMT+01:00 Max Leske <maxleske@gmail.com>:
Very, very cool Sven (and Henrik!)!
And it even works with exotic objects: { 1@2. 2@3 } numbers sum ââ> (3@5)â
I really not understand how it helps with original problem. There was example with colors sum which was stop people to allow sum on empty collection. How extra message solve it?
On 07 Jan 2016, at 14:08, Denis Kudriashov <dionisiydk@gmail.com> wrote:
2016-01-07 13:14 GMT+01:00 Max Leske <maxleske@gmail.com>: Very, very cool Sven (and Henrik!)!
And it even works with exotic objects: { 1@2. 2@3 } numbers sum ââ> (3@5)â
I really not understand how it helps with original problem. There was example with colors sum which was stop people to allow sum on empty collection. How extra message solve it?
(You should look at the code ;-) The example is confusing and not really relevant (because the points are not numbers). Basically, sending #numbers to a collection returns an object, called a 'collection operations' object (just a wrapper) that offers an interface to do more operations on the collection, with the additional assumption that it now contains (real) numbers. #(1 2 3) numbers sum => 6 #() numbers sum => 0 #(1 2 3) numbers product => 6 #() numbers product => 1 #(1 2 3) asSet magnitudes max #() asSet magnitudes maxIfEmpty: [ 100 ] But again, the code was just me thinking out loud, inspired by what Henrik did, I am totally not sure it is a really good idea. Sven
2016-01-07 14:24 GMT+01:00 Sven Van Caekenberghe <sven@stfx.eu>:
Very, very cool Sven (and Henrik!)!
And it even works with exotic objects: { 1@2. 2@3 } numbers sum ââ> (3@5 )â
I really not understand how it helps with original problem. There was example with colors sum which was stop people to allow sum on empty collection. How extra message solve it?
(You should look at the code ;-)
The example is confusing and not really relevant (because the points are not numbers).
Basically, sending #numbers to a collection returns an object, called a 'collection operations' object (just a wrapper) that offers an interface to do more operations on the collection, with the additional assumption that it now contains (real) numbers.
#(1 2 3) numbers sum => 6 #() numbers sum => 0 #(1 2 3) numbers product => 6 #() numbers product => 1
#(1 2 3) asSet magnitudes max #() asSet magnitudes maxIfEmpty: [ 100 ]
But again, the code was just me thinking out loud, inspired by what Henrik did, I am totally not sure it is a really good idea.
Ok. I see it. And I like It.
On 20 Dec 2015, at 12:59, Max Leske <maxleske@gmail.com> wrote:
I would like to wrap up this discussion.
Good idea.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Excellent summary, thanks.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes
2. Do you agree to the removal of #detectSum:?
yes
3. Do you agree to the removal of #sumNumbers?
yes
4. Do you agree that the #max and #min selectors also need to be adapted?
probably yes.
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
You should exclude your setup out of your actual benchmark, like this: | numbers | numbers := (1 to: 1000000) asArray. [ numbers sum ] benchFor: 10 seconds. "a BenchmarkResult(126 iterations in 10 seconds 77 milliseconds. 12.504 per second)"
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
Updated benchmarks with pre-calculated collection of numbers (as suggested by Sven): Benchmarks of the current versions: [ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 124 iterations, 12.389 per second [ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 109 iterations, 10.801 per second) [ (1 to: 100) asArray sum ] benchFor: 10 seconds. 4,166,154 iterations, 416,532 per second [ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 3,115,121 iterations, 311,481 per second Benchmarks of the new versions: [ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 125 iterations, 12.490 per second [ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 119 iterations, 11.842 per second [ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 130 iterations, 12.947 per second [ (1 to: 100) asArray sum ] benchFor: 10 seconds. 2,985,085 iterations, 298,449 per second [ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 3,260,280 iterations, 325,963 per second [ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 3,143,812 iterations, 314,287 per second The benchmark for #sum suggests that thereâs indeed a benefit of duplicating the code for that particular case (the delegations would make #sum 30% slower). I still think we should use delegation there instead of duplicate code. #sum is not a âcriticalâ method in my opinion. Cheers, Max
On 20 Dec 2015, at 14:01, Sven Van Caekenberghe <sven@stfx.eu> wrote:
On 20 Dec 2015, at 12:59, Max Leske <maxleske@gmail.com <mailto:maxleske@gmail.com>> wrote:
I would like to wrap up this discussion.
Good idea.
On 05 Dec 2015, at 18:14, stepharo <stepharo@free.fr> wrote:
So what is the conclusion? I like the idea of Esteban M to have iterator because it moves some behavior out of core classes.
[[[
aCollection arithmetic sum: [...] or.... aCollection arithmetic avg.
]]]
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Excellent summary, thanks.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes
2. Do you agree to the removal of #detectSum:?
yes
3. Do you agree to the removal of #sumNumbers?
yes
4. Do you agree that the #max and #min selectors also need to be adapted?
probably yes.
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
You should exclude your setup out of your actual benchmark, like this:
| numbers | numbers := (1 to: 1000000) asArray. [ numbers sum ] benchFor: 10 seconds.
"a BenchmarkResult(126 iterations in 10 seconds 77 milliseconds. 12.504 per second)"
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
Hi My vote:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes but I prefer #sum: return zero too for empty collections
2. Do you agree to the removal of #detectSum:?
yes
3. Do you agree to the removal of #sumNumbers?
yes
4. Do you agree that the #max and #min selectors also need to be adapted?
yes
2015-12-20 12:59 GMT+01:00 Max Leske <maxleske@gmail.com>:
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
there is no #sumNumbers, only #sumNumbers:
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
yes
2. Do you agree to the removal of #detectSum:?
yes
3. Do you agree to the removal of #sumNumbers?
yes
4. Do you agree that the #max and #min selectors also need to be adapted?
hm. I like the "detect". If aCollection detectMax: aBlock becomes aCollection max: aBlock it looks too similar to aCollection size max: aValue But I could live with that :)
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
Le 20/12/2015 12:59, Max Leske a écrit :
I would like to wrap up this discussion.
While I think that iterators are an intriguing idea I also believe that they are beyond the scope of this issue. If anybody wants to follow up on iterators (or unit types for that matter) please start a new thread / issue.
I propose to use Svenâs version for #sum:ifEmpty:. The result would be these three methods:
sum ^ self sum: [ :each | each ] ifEmpty: [ 0 ]
sum: aBlock ^ self sum: aBlock ifEmpty: [ self errorEmptyCollection ]
sum: aBlock ifEmpty: emptyBlock | sum sample | self isEmpty ifTrue: [ ^ emptyBlock value ]. sample := self anyOne. sum := self inject: sample into: [ :accum :each | accum + (aBlock value: each) ]. ^ sum - sample
Iâve attached a couple of benchmark results below. To me they show that 1. the new implementation is maybe a tiny bit slower but insignificantly so (if youâre going for performance youâll probably write your own optimised version anyway) 2. there is no need to duplicate the code (like #sum and #sum: currently do). Itâs perfectly fine to delegate to #sum:ifEmpty:
In addition to the above changes I would like to remove #detectSum: (-> #sum:) and #sumNumbers (-> #sum).
Note that once we agree on changing this API, we will need to also change #detectMin:, #detectMax:, #min, #max as well as all overrides (e.g. RunArray, Interval) of these and of #sum et. al. to stay consistent. The changes would of course be in line with this change, such that every operation has a unary selector with a sensible default, one that takes a block and throws an error for empty collections and a third that takes a block for the iteration and one for the empty case.
Please cast your vote for these changes:
1. Do you agree to changing #sum and #sum: in the suggested way?
Yes
2. Do you agree to the removal of #detectSum:?
Yes
3. Do you agree to the removal of #sumNumbers?
Yes
4. Do you agree that the #max and #min selectors also need to be adapted?
Yes
Thanks for you help.
Cheers, Max
Benchmarks ============ (Note that these arenât very good benchmarks. Thereâs quite some variation on each run.)
Machine: MacBook Pro (15-inch, Early 2011) CPU: 2.2 GHz Intel Core i7 Memory: 8 GB 1333 MHz DDR3 Disk: APPLE SSD TS512C (500 GB)
Benchmarks of the current versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 75 iterations, 7.470 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 72 iterations, 7.128 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,189,477 iterations, 118,912 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,171,467 iterations, 117,112 per second
Benchmarks of the new versions:
[ (1 to: 1000000) asArray sum ] benchFor: 10 seconds. 73 iterations, 7.244 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 75 iterations, 7.480 per second
[ (1 to: 1000000) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 72 iterations, 7.141 per second
[ (1 to: 100) asArray sum ] benchFor: 10 seconds. 1,115,827 iterations, 111,560 per second
[ (1 to: 100) asArray sum: [ :e | e ] ] benchFor: 10 seconds. 1,154,595 iterations, 115,425 per second
[ (1 to: 100) asArray sum: [ :e | e ] ifEmpty: [ 0 ] ] benchFor: 10 seconds. 1,102,358 iterations, 110,203 per second
-- Cyril Ferlicot http://www.synectique.eu 165 Avenue Bretagne Lille 59000 France
participants (21)
-
Ben Coman -
Chris Cunningham -
Denis Kudriashov -
Eliot Miranda -
Esteban A. Maringolo -
Ferlicot D. Cyril -
Gabriel Cotelli -
Guillermo Polito -
Henrik Nergaard -
J.F. Rick -
Max Leske -
Nicolai Hess -
Nicolas Cellier -
Peter Uhnak -
Robert Withers -
Stephan Eggermont -
stepharo -
Sven Van Caekenberghe -
Thierry Goubier -
Tudor Girba -
Yuriy Tymchuk