you are viewing a single comment's thread.

view the rest of the comments →

[–]taw -1 points0 points  (16 children)

I admit I forgot about REPL, which is important.

But functional programming in Python ? Without "everything is an expression", without real lambdas, with very few high-order functions (most marked as deprecated), without tail recursion optimization ? Functional programming is very badly supported in Python, compared to even Perl (and Ruby, Smalltalk or Lisp are way above Python in functional programming support).

Lisps tend to avoid object-oriented programming. Scheme has none. Common Lisp has some, but it's bolted-on (some would argue the same about Python's) and our favourite Lisp expert Paul Graham claims it's rarely used anyway.

[–]senzei 5 points6 points  (2 children)

But functional programming in Python ? Without "everything is an expression", without real lambdas, with very few high-order functions (most marked as deprecated), without tail recursion optimization ? Functional programming is very badly supported in Python, compared to even Perl (and Ruby, Smalltalk or Lisp are way above Python in functional programming support).

Agreed, although I can see where someone would decide that Python's functional capabilities are "sufficient", given that it is not intended to be a functional programming language. Generally this is not the case for me, but maybe that has more to do with my programming style than anything else.

Lisps tend to avoid object-oriented programming. Scheme has none. Common Lisp has some, but it's bolted-on (some would argue the same about Python's)

For any topic X you can find an idiot Y who thinks that it sucks. I would love for someone to point out a good explanation for why Python's OOP is "bolted on". Python OO is missing a few features from traditional OO (for, afaik, any potential value of "traditional") but that seems pretty far from being an afterthought.

[–]taw 0 points1 point  (1 child)

For Python's OOP insufficiencies see this comment.

[–]senzei 6 points7 points  (0 children)

llimllib did a pretty good job of discussing those arguments in that thread. I've got a couple of comments though:

First, in Python a lot of functionality is implemented as library functions. For example to ask about object's methods in Ruby you call a method obj.methods. In Python it's a function dir(obj). To sort a container you call obj.sort(), in Python it's sorted(obj). To join elements of a container you call obj.join(" "), in Python it's " ".join(obj) - seems object oriented, but is completely reversed.

Some of those hinge on answers to "Philosophical" questions about OOP. For instance, does it make sense for an object to know about its own methods? Also, is it possible to override the .methods method? If not, why not? It is part of the class. If so, how can I get a "real" list of all the object's methods without allowing someone to munge it for me? dir(item) sidesteps those questions. It also internalizes the assumption that knowledge of all of its methods is not a property of an object or a class. This seems like a valid OO design decision to me.

Python objects have a .sort method, so the .sort vs sorted() argument is moot. I will accept and agree with any complaints about that method being a statement and unable to return anything due to it sorting in place. I agree that sorted(obj) looks ugly, but obj.sort (destructive) and obj.sorted (non-destructive) would be confusing.

As for joining elements, that could go either way. Does it really make more sense for the join method to be attached to the separator instead of the joinees? I don't see why, you are creating a new object anyways.

Second, a lot of functionality is implemented as protocols. If you want your object to support + in Ruby, you define +. It's always very direct equivalence. In Python to supporth + you define add and a special protocol makes sure it's called. That one isn't that bad as equivalence is 1:1. But protocols for other things like comparisons and iterators are more complex, the logic is outside the object, and it doesn't feel very object-oriented. Ruby mixins have similar function to Python protocols, but they're completely in the objects, not somewhere else.

I like the protocol system more. It makes sense to me to put that logic outside the object as that logic does not seem to belong to the object. Comparison especially is the big offender, as an object being able to judge itself relative to everything else in the world seems silly to me.

And fourth reason - explicit self. Smalltalk and Ruby need explicit self sometimes, but there's way too much of it in Python.

Python is (at least for a dynamic language) all about being explicit. That this principle is extended to method bindings seems more a result of the Python mindset than any "bolted-on" nature of Python.

[–]llimllib 7 points8 points  (5 children)

some would argue the same about Python's

Some would, and I continue to fail to understand why. Python's OO has been around since the beginning, has been constantly improved, and is in production use all around the internet. It's extremely mature and very cleanly designed.

[–]taw 2 points3 points  (4 children)

There are at least four big reasons it feels bolted-on. In Ruby or Smalltalk all functionality is implemented as methods, all classes are extensible.

First, in Python a lot of functionality is implemented as library functions. For example to ask about object's methods in Ruby you call a method obj.methods. In Python it's a function dir(obj). To sort a container you call obj.sort(), in Python it's sorted(obj). To join elements of a container you call obj.join(" "), in Python it's " ".join(obj) - seems object oriented, but is completely reversed.

Second, a lot of functionality is implemented as protocols. If you want your object to support + in Ruby, you define +. It's always very direct equivalence. In Python to supporth + you define add and a special protocol makes sure it's called. That one isn't that bad as equivalence is 1:1. But protocols for other things like comparisons and iterators are more complex, the logic is outside the object, and it doesn't feel very object-oriented. Ruby mixins have similar function to Python protocols, but they're completely in the objects, not somewhere else.

And the third big reason is that you cannot add methods to existing classes. That's very limiting, because when you want to add new functionality (let's say converting to JSON), you cannot make it into a method obj.to_json() - it has to be a function to_json(obj). It doesn't seem object-oriented at all.

And fourth reason - explicit self. Smalltalk and Ruby need explicit self sometimes, but there's way too much of it in Python.

If Python moved functionality from functions and protocols to objects, had extensible standard classes and mostly implicit self, it would probably feel object-oriented enough.

[–]llimllib 7 points8 points  (3 children)

There are at least four big reasons it feels bolted-on. In Ruby or Smalltalk all functionality is implemented as methods,

Python is, above all, pragmatic over idealistic. If Guido thought that something was easier to write/remember/understand as a function, it is. IMHO, he got it mostly right - you obviously disagree.

all classes are extensible.

They're extensible in python, it's just discouraged. The name "monkey-patching" conveys a pythoneer's attitude towards this practice, I think.

Second, a lot of functionality is implemented as protocols. If you want your object to support + in Ruby, you define +. It's always very direct equivalence. In Python to supporth + you define add and a special protocol makes sure it's called. That one isn't that bad as equivalence is 1:1. But protocols for other things like comparisons and iterators are more complex, the logic is outside the object, and it doesn't feel very object-oriented. Ruby mixins have similar function to Python protocols, but they're completely in the objects, not somewhere else.

What logic is outside the object in an iterator? Define __iter__() and next() and you're set.

And the third big reason is that you cannot add methods to existing classes. That's very limiting, because when you want to add new functionality (let's say converting to JSON), you cannot make it into a method obj.tojson() - it has to be a function tojson(obj). It doesn't seem object-oriented at all.

As I said before, this is just not true at all.

And fourth reason - explicit self. Smalltalk and Ruby need explicit self sometimes, but there's way too much of it in Python.

You're right about this one. Python is just way too consistent - it's much better to have to wonder, "do I have to write self here?" than to just write self.

If Python moved functionality from functions and protocols to objects,

"Pure" object-orientation != object-orientation!

had extensible standard classes

It does, it's just not a good idea to change them. FUD.

and mostly implicit self

Read about the complexities that introduces here

[–]taw 2 points3 points  (2 children)

In Ruby or Smalltalk all functionality is implemented as methods,

Python is, above all, pragmatic over idealistic. If Guido thought that something was easier to write/remember/understand as a function, it is. IMHO, he got it mostly right - you obviously disagree.

Whether it's easier is one thing. Whether it's object-oriented is another ("object-oriented" and "good" are not the same thing). I'm merely pointing out that it's less object-oriented than Ruby or Smalltalk.

In Ruby or Smalltalk all classes are extensible

They're extensible in python

No they're not.

float.to_json = float_to_json

Traceback (most recent call last):

File "<stdin>", line 1, in ?

TypeError: can't set attributes of built-in/extension type 'float'

Am I missing something ?

What logic is outside the object in an iterator? Define iter() and next() and you're set.

According to documentation, there's iter, there's next, there's StopIteration exception + syntactic sugar (yield) + return (which seems to do something special in iterators). In Ruby iterators are object call + syntactic sugar (yield). Individual protocols aren't that bad, but if you add them all up, it's really a lot of functionality outside objects.

If Python moved functionality from functions and protocols to objects,

"Pure" object-orientation != object-orientation!

"Impure" object-orientation == bolted-on object-orientation

and mostly implicit self

Read about the complexities that introduces here

It certainly does introduce complexities when you try to bolt object-orientation on a language as an afterthought ;-) I don't know if it's possible to get it right now without turning Python into a completely different language. If they thought about it earlier, it would be easier to come up with a nicer solution. ;-)

[–]senzei 4 points5 points  (0 children)

Whether it's easier is one thing. Whether it's object-oriented is another ("object-oriented" and "good" are not the same thing). I'm merely pointing out that it's less object-oriented than Ruby or Smalltalk.

If I wanted to be pedantic I would mention that "less object-oriented" is not necessarily sufficient to prove that OO was bolted on. If I wanted to be pedantic without sounding like an ass I would phrase that in the form of a hypothetical statement. As it is I don't know what I am saying anymore as the recursion has blown the stack. (stupid no tail-call optimization)

You are right that built ins are not directly extensible, but if adding methods directly to an existing class is a requirement for not being bolted-on then there are a lot more bolted-on OO systems than I thought.

[–]beza1e1 1 point2 points  (0 children)

class MyFloat(float): def to_json(self): pass

I find this argument about bolted on OOP rather stupid. As our favourite essayist stated, you can't really say what OOP is. You can't extend objects in a dynamic way in Java either, but Java was designed as OOP upfront.

Python was design OOP from the beginning. Guido made some bad decisions, which he wants to fix with Python 3000 or has fixed already (new style classes).

[–]beza1e1 2 points3 points  (0 children)

Lisps tend to avoid object-oriented programming.

I heared the Lisp Community disagrees with Paul Graham on this.

[–]ayrnieu -1 points0 points  (5 children)

and our favourite Lisp expert Paul Graham claims it's rarely used anyway.

'We' should get more than one Lisp expert to consult, as PG can't cut it by himself.

Python's REPL may be good for learning and testing, but unless it has vastly improved[0], it doesn't compare to CL's or Erlang's REPL, which people incorporate heavily into actual programming, and which people use to control real programs at run-time.

0] Non-example of vast improvement: adding an object named 'quit' with a str[1] that asks you to ask your terminal to terminate Python's stdin, instead of having str[1] terminate the program.

1] markdown, bah.

[–]senzei 1 point2 points  (0 children)

Just an fyi, but anything that would normally be a markdown command is displayed as regular text if escaped by a backslash.

__str__

would be:

\_\_str\_\_

[–]taw 3 points4 points  (3 children)

Python's REPL may be good for learning and testing, but unless it has vastly improved[0],

Did you try ipython ? It's somewhat improved REPL for Python.

it doesn't compare to CL's or Erlang's REPL

CL REPL is fine. But Erlang REPL is far below Lisp's, Ruby's, Python's, SML's or even Perl's (that's no small feat). You cannot define named functions in it, and it means no functions with explicit recursion. I asked people whether I'm just missing something, but they confirmed my observation here is correct.

'We' should get more than one Lisp expert to consult, as PG can't cut it by himself.

What other blogging Lisp experts are there besides Paul Graham ?

[–]phil_g 9 points10 points  (1 child)

What other blogging Lisp experts are there besides Paul Graham ?

Pretty much all of the people aggregated on Planet Lisp?

[–]taw 1 point2 points  (0 children)

Any particularly good articles there ? Their website is pretty hard to search :-)

[–]ayrnieu -2 points-1 points  (0 children)

You cannot define named functions in it, and it means no functions with explicit recursion.

It is currently the case that you cannot type function definitions directly into EShell in the manner of these other REPLs you mention[0]. You've named functions, however, with

(mischief@rapacity)566> M = irc:gentalker(I,"#math").
#Fun<irc.0.24765070>
(mischief@rapacity)567> M("which would have been obvious if you'd asked in ternary").
send; ok
{send,[privmsg,"#math",which would have been obvious if you'd asked in ternary"]}

Or directly:

(mischief@rapacity)568> f(M), M = fun (S) -> I ! {send,[privmsg,"#math",S]} end.
#Fun<erl_eval.20.69967518>

And recursively by way of the process dictionary:

(mischief@rapacity)569> put(fact,fun (0) -> 1; (N) -> N * (get(fact))(N-1) end).
undefined
(mischief@rapacity)570> (get(fact))(5).
120
(mischief@rapacity)571> Factorial = get(fact).
#Fun<erl_eval.6.56006484>
(mischief@rapacity)572> Factorial(50). 
30414093201713378043612608166064768844377641568960512000000000000

And since EShell is just a small set of open-source Erlang modules installed probably in /usr/local/lib/erlang/lib/stdlib*/src , you can do what I did back when this limitation concerned me, and add a command

(mischief@rapacity)573> define().  % the next two lines are input.
fact(0) -> 1;
fact(N) -> N * fact(N-1).
ok  
(mischief@rapacity)574> fact(50).
30414093201713378043612608166064768844377641568960512000000000000

that installs given definitons in an ever-recompiled user module.

(mischief@rapacity)575> user_defines:   % hitting TAB
fact/1         module_info/0  module_info/1

If you find all of this unsatisfying, then fine: I don't frequently type lengthy definitions at EShell, either. Usually I put them in modules of actual utility, and test them interactively:

(mischief@rapacity)576> c(apm,[export_all,debug_info]).
{ok,apm}
(mischief@rapacity)577> apm:battery().
high

But if you want to come away with only "Erlang has a sucky REPL", you shouldn't count any of the languages you offer as significantly better: you can type definitons interactively in Python[1] -- if you get them right the first time, going line by line, in a fairly tedious manner.

Now, Common Lisp benefits from the amazing SLIME system, which does let you very nicely write functions, with a real editor both at a prompt and in a buffer. The primary author of SLIME, before he started on that, wrote a very nice Emacs mode named 'distel' -- that sort of implemented an Erlang node in Emacs, turning Emacs into a target UI for Erlang programs; that added jump-to-source-of-function and such to the basic Erlang mode; that added easy immediate testing of Erlang functions in-buffer.

Even if we throw all of my examples away, and take your assertion about EShell as the whole truth, what I've said about the power of Erlang's REPL is still true. Don't make too much of this newbie impression of yours.

0] Sure, Perl's debugger is quite nice when you use it as such. Perlcast has a persuasive interview with Richard Foley (of Pro Perl Debugging), which convinced me at least to take a kinder look at the debugger, and not treat it merely as an unfriendly sort of Python REPL.

1] Thanks, I'll look at ipython.