Live data from Hacker News

My Objection to Array#sum

github.com

61–65 of 65 posts

Re: My Objection to Array#sum

#61
post #6

Python does this - sum/any/all are standalone functions, and join() is a member of the string class instead of the array class. Python also gets a lot of flack for it, as it seems like every month you can see someone complaining about why you use `"\n".join(lines)` to join an array instead of `lines.join("\n")`. People can't seem to wrap their head around it. Personally, I think the big mistake is to think objects sh…

Count me as one of those people that don't get it. If I'm joining the elements of an array together, I'm operating on the array. Therefore I _expect_ the join() method to be on the array. To me, Python can't seem to make up its mind with these weird (IMHO) stand-alone methods like join(), sum(), any(), etc. That's the kind of thing I'd expect in a functional language, not in an OO language.

The reason join() isn't a method on lists is that it would mean that every class that wants to act like a list would have to implement it (or inherit from someone who does).

If you write your own class that is iterable but doesn't inherit from list, it would not have a join() method unless you wrote one. But it works as an argument to '\n'.join() for free since that can operate on anything iterable.

The other problem with sticking methods like join(), sum() and any() onto collections is that whenever a new method is added (for example, any() was added in python 2.5), it would cause confusion for anyone who has a list-like class that happens to have a method with that name that does something different.

Re: My Objection to Array#sum

#62
post #35

Earlier quoted context omitted.

"\n".join(lines) is confusing because you expect the complicated thing to be the object, and the simple thing to be the argument. The confusion is very basic. It would be cool but probably perlishly unadvisable if join,sum,any,all were just implemented as "\n".join(lines) -> lines.join("\n") sum(alist) -> alist.join(+) any(alist) -> alist.join(or) all(alist) -> alist.join(and)

That's what Haskell does. Your universal joiner is called "fold", sometimes pronounced "reduce" (Scheme/Python) or "inject" (Smalltalk/Ruby). From the Haskell prelude: sum = foldl (+) 0 product = foldl (*) 1 and = foldl (&&) True or = foldl (||) False any p = or . map p all p = and . map p concat = foldl (++) [] unlines = concat . map (++ "\n") Google calls it "reduce" as well - this is what MapReduce is based upon.…

Yeah, in practice though I would rarely use reduce in python to do a sum/any/all, but now that they're in the language I like them. Before I would just spell it all out. It was just slightly too wordy to make it worth it, kind of like python lambdas in general.

Re: My Objection to Array#sum

#63
post #44
post #20

Earlier quoted context omitted.

Well join should be on sequence instead of list then. To me it seems unnatural to see join on string because string feels more 'basic' than a sequence, so I would expect the sequence to know about strings rather than the opposite. But the argument against join on string is not as strong as the argument against sum on array.

I anticipated this objection if you go back and re-read my post, but I'll blow out the point a bit more here: In Java terms, "sequence" is an interface. You can't put join there. Because then you have to put add, any, none, all, reduce, filter, and so on and so forth for at least another ten or twenty base methods (ignoring everything else that applies just to the sequence protocol written by users), which every sing…

Thankfully there is this concept of mixins (and, gasp, multiple inheritance), to solve some of these issues.

Re: My Objection to Array#sum

#64
I see the point, but I disagree that it's a problem that needs to be solved. What is the alternative? A "Numbers" class? And how would this look in syntax? Numbers.new([ 0, 1, 2, 3 ])? I mean, I'm sure you could make it a special case, but it really isn't a special case. Type inference won't cut it, and having to specify everything explicitly is tedious (and a matter of where you draw the line).

And what would you get if you mapped over a Numbers object? An Array or another Numbers object? Looks like you'll have to specify the return type for the block. Even then, is an array of numbers really always a Numbers object?

I suppose that in this case, you could use a Numbers module and (somehow) make every array of numbers magically inherit this. But a generic, extensible way of doing this would be really, really complex and hard to do efficiently. Does every newly created collection have to be checked for Numbers-ness?

Really, you can go as wild as you want to, but I'd rather just be able to map, transpose and flatten around without all of this bureaucracy. Besides that, I'm fairly certain this is only a problem in single-dispatch languages where methods are owned by classes.

Re: My Objection to Array#sum

#65
post #51

Earlier quoted context omitted.

In Ruby, this would be: class Array def detectSum(&block) self.map(&block).sum end end >> [1, 2, 3].detectSum {|i| i**2} => 14 Most Ruby people though would just call #map and #sum in succession on an ad hoc basis.

Ew, how inefficient. Here is the Smalltalk code: detectSum: aBlock "Evaluate aBlock with each of the receiver's elements as the argument. Return the sum of the answers." | sum | sum := 0. self do: [:each | sum := (aBlock value: each) + sum]. ^ sum Anyway, my point still stands. Better to accept a block than to use a sum method that assumes the Array consists of numbers

I agree that using blocks/closures is a powerful and useful mechanism -- In fact I think that they are a great implementation technique for this method.

What this comes down to for me is: what makes the best interface?

Specifically:

  myArray.sum()   
is much easier to read and use. In a world where we have to maintain our code much longer then we write it this simplicity is very valuable.

Secondly, it is important to note that since the array uses generics it is not necessary to check that the items are numbers, as any attempt to insert a non-number would have thrown an error.

So in summery I agree with you completely that array.sum should _not_ exist. But I hope you see my point that array.sum _should_ exist. (If the code enforces the numbers to exist in the array there is no reason not to include the sum method!)

Post reply on HN