Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The main reason why I like having functions instead of methods is that it removes the possibility for inconsistency. In some languages, you have some objects with methods named size, some with length, etc. With python, you just have one global len method to remember.


I am often frustrated by this as well, but I'm not sure it's specific to Python or Ruby. Why is it Math.log(1) rather than 1.log()? And given whatever reason it's Math.log, why is it a.inverse() when a is a Matrix not MatrixMath.inverse(a)?

Is there a good guideline for static method vs. instance method distinction?


In python the convention tends to be:

  a.inverse() -> mutates 'a' to its inverse
  MatrixMath.inverse(a) -> returns the inverse of 'a', leaving 'a' unchanged


1 isn't a matrix. Integers/floating point numbers are in many languages treated differently than other types for reasons of performance and memory management etc. Ruby isn't different in this respect (IIRC they call it immediate objects). A matrix is a "normal" object in ruby.

BTW the ruby equivalent for the above convention would be (maybe inspired by scheme?)

    a.inverse!() -> mutate a
    a.inverse() -> return the inverse
Which one is more consistent?


I am a python user, but I _really_ like that .inverse!() notation indicating that the object itself is being mutated. It's concise and consistent. Sometimes punctuation in names is exactly what you need. Same with ? for predicates. I'll take even? over evenp any day of the week.


  1 isn't a matrix
A Fixnum could be interpreted as a 1x1 matrix. For some recursive solutions, it would probably be nice of MatrixMath.inverse accepted it as such, as well as [[1]].


This confuses me, because python strings use str.lower() to return a lowercase copy of str, leaving str unchanged. I prefer the Ruby idiom of naming the mutating versions with a !.


I agree with you, and I was equally frustrated. I think the problem is that most people don't use the Math module that much. Hence they try to keep the Numeric classes smaller rather than filling them with all possible Math functions.

However, it doesn't take much to "fix" it the way you like it. You need to patch Numeric. Whenever you call a method that doesn't exist, it calls the method "method_missing". Hence you can monkey patch Numeric#method_missing to call the equivalent Math function.


"In some languages, you have some objects with methods named size, some with length, etc."

In ruby you dont. It's #size and ruby is quite consistent in this respect. So it's remembering a method name vs remembering a function name (in a supposedly OO language).


Actually in Ruby you can often use both #size and #length.

It's part of the Ruby's philosophies to have synonyms like that.


Then why bother with OOP to begin with?


Having a global len() function is very, very linked to polymorphism. You can call it on anything that has a __len__() function defined (i.e., any class that implements something like a "HasLength" interface in Java terms).


If you have __len__() implemented on the object, why not call it directly? If you are dealing with object X, that is of classes A,B,C or D (that all implement __len__), I don't see the fundamental difference between len(X) and X.__len__().


The "magic methods" wrapped in in double-underscores are special because other parts of the language's syntax rely on them always meaning the same thing. (Some room for programmer misbehavior here.)

Example: If __len__ is defined on an object x, and __nonzero__ (Py2) or __bool__ (Py3) is not defined, then "not x" will test if x.__len__() == 0.

Similarly, __contains__ interacts with "foo in x", and __eq__ and __hash__ are expected to play well together for hashable objects (e.g. unique members of a set).

The double underscores are a useful code smell that indicates you're changing how an object interacts with Python's syntax.


I've been puzzled by len() and company for a long time and this is the most complete and sensible explanation I've heard. Thank you.

I still think it's the wrong design, mind you, but I no longer think it's a crazy one.


Tomato, tomahto.

Why use x.__len__() rather than len(x)?


There really is no difference except that len() will raise an error if no __len__ method is found on the object.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: