My adventures in Ruby.
an autobyography by rt8396
After breaking free from the wild love affair i had with Python, a new
day had dawned. I dried my eyes and soon discovered the Ruby language
(not by choice however) --now don't worry fellow Pythonistas, i am
still on your side (mostly) -- but i now realize Ruby has some good
things going for it. For instance...
1.) No need to use "()" to call a function with no arguments.
Python --> "obj.m2().m3()" --ugly
Ruby --> "obj.m1.m2.m3" -- sweeet!
Man, i must admit i really like this, and your code will look so much
cleaner.
2.) the .each method
container.each{|localVar| block}
This method can really cleanup some ugly for loops, although i really
like the readability of for loops.
3.) true OOP
Now before you go and get all "huffy" over this statement, hear me
out. Python is the best language in the world. But it damn sure has
some warts! "len(this)" instead of "obj.length" max(that) instead of
[1,2,3,4,5].max(). You know what i am talking about here people. We
all get complacent and It seems easier to just cope with these
problems instead of fighting for change. But look at the French, WHAT
THE HELL HAS THAT DONE FOR THEM, *NOTHING*!!!!
As for the rest of Ruby, i am not impressed. The redundant usage of
"end" over indention perplexes me. The Perlish feel of "require" and
the horrifically cryptic idioms of Ruby regular expressions. The
"puts" and "gets" seem childish and the math class does not even have
a degrees or radians function!
Anyway, i thought i would get this off my chest and catch up with old
friends in the meantime. We can all make Python the perfect language
it needs to be, but it ain't gonna be easy!
Thank you all
PS stay tuned for more from this series....
How do you distinguish between calling a method with no arguments, and
getting access to the method object itself (because it _is_ an object,
y'know, it's OO all the way down...)?
> 2.) the .each method
> container.each{|localVar| block}
> This method can really cleanup some ugly for loops, although i really
> like the readability of for loops.
map(lambda localVar: <block>, sequence)
or:
def usefully_named_func(var):
<block>
return var
transformed = [usefully_named_func(v) for v in sequence]
> 3.) true OOP
> Now before you go and get all "huffy" over this statement, hear me
> out. Python is the best language in the world. But it damn sure has
> some warts! "len(this)" instead of "obj.length" max(that) instead of
> [1,2,3,4,5].max().
As the Zen says: '[P]racticality beats purity'. Personally, I'm not
sure how a handful of convenient built-in functions make a language in
which _everything is an object_ somehow "false" OO.
If you're really that concerned with writing "true" OO (for some
wildly variable value of "true"), there's nothing stopping you from
doing so now:
obj.__len__()
With max(), this is a built-in that takes _any_ iterable and an
optional key function, and returns the highest value as per the key.
This means that _every_ iterable object - as _well_ as every object
that supports enough of the iterator protocol - can be handed to max()
and a result obtained. So at best, I just need to make sure my new
sequence-type provides the iterator protocol and viola, it works with
max() without me having to hand-code a .max() that's specialised for
my new type, and without Python forcing me to have a complex
inheritance chain just to make sure I include the right
MaxableSequence ancestor to inherit the right .max().
> PS stay tuned for more from this series....
Is this going to be more of you telling us - without any apparent
irony whatsoever - how Ruby has some valid points after all your
vilification last year when we said the same to you? If so, where can
I sign up?!
(You should consider trading guest spot posts with Xah Lee on your
respective blogs. You both have a very similar approach to programming
& programming languages and I think the synergy would be amazing to
see.)
IIRC from the Pickaxe, to get at a method object, you call a method on
the object whose specific job it is to return method objects.
Unsurprisingly, this method is itself named "method". So:
foo.bar #calls the "bar" method on foo
foo.method(:bar) #returns the method "bar" of foo, as an object
Cheers,
Chris
--
http://blog.rebertia.com
Here is some wisdom from Guido van Rossum on the matter:
http://mail.python.org/pipermail/python-dev/2006-May/064841.html
Carl Banks
> My adventures in Ruby.
Oh, it's you.
Good boy.
Now, why don't you have a look at javascript and come back in six months?
Or better yet, haskell and twelve months.
thanks
>> 2.) the .each method
>> container.each{|localVar| block}
>> This method can really cleanup some ugly for loops, although i really
>> like the readability of for loops.
>
> map(lambda localVar: <block>, sequence)
>
> or:
>
> def usefully_named_func(var):
> <block>
> return var
>
> transformed = [usefully_named_func(v) for v in sequence]
>
The issue here is of course that `map` and comprehensions are
transformations. `#each` exists for effectful iterations (Ruby has
`#map` for the map operation). So the intent expressed by `#each` and
`map` isn't the same. Furthermore and this is the most problematic
limitation of Python here, `lambda` doesn't allow complex
transformations due to its restrictions, so one has to switch to named
functions which works but isn't sexy (and tends to lower readability
imo).
>> 3.) true OOP
>> Now before you go and get all "huffy" over this statement, hear me
>> out. Python is the best language in the world. But it damn sure has
>> some warts! "len(this)" instead of "obj.length" max(that) instead of
>> [1,2,3,4,5].max().
>
> As the Zen says: '[P]racticality beats purity'. Personally, I'm not
> sure how a handful of convenient built-in functions make a language in
> which _everything is an object_ somehow "false" OO.
>
That's an interesting point, but not relevant at the end of the day:
`foo.length` and `length(foo)` have the same "practicality". On the
other hand Ruby can be praised for the coherence: everything's a
method period end of the story; while Python does have a dichotomy
between methods and functions/generic methods (whether or not that
dichotomy bothers users is another issue).
> With max(), this is a built-in that takes _any_ iterable and an
> optional key function, and returns the highest value as per the key.
> This means that _every_ iterable object - as _well_ as every object
> that supports enough of the iterator protocol - can be handed to max()
> and a result obtained. So at best, I just need to make sure my new
> sequence-type provides the iterator protocol and viola, it works with
> max() without me having to hand-code a .max() that's specialised for
> my new type, and without Python forcing me to have a complex
> inheritance chain just to make sure I include the right
> MaxableSequence ancestor to inherit the right .max().
>
Well interestingly, Ruby works pretty much the same way. Where Python
asks that you implement the iterator protocol to have `max` work, Ruby
asks that you implement the `each` method (also called the Enumerable
protocol) and mixin `Enumerable` (http://ruby-doc.org/core/classes/Enumerable.html
). Then you get max (and min, map, etc…) "for free". Of course you're
free to re-implement all of them manually if you wish to do so (or if
you need those operations to return your custom collection rather than
arrays). Enumerable is merely a convenience mixin. So there's no big
difference here (and note that mixins aren't necessarily part of the
inheritance chain, though they're implemented that way in Ruby).
Also, it's "voilà" not "viola" (a viola is a bowed string instrument,
slightly bigger than a violin, whereas voilà is a french interjection
used to call attention or suggest the appearance of something, as if
by magic).
>> PS stay tuned for more from this series....
>
> Is this going to be more of you telling us - without any apparent
> irony whatsoever - how Ruby has some valid points after all your
> vilification last year when we said the same to you? If so, where can
> I sign up?!
I fear I'll have to agree with that sentiment.
"well, the main difference between a function and a method (from the
caller's pow) is a syntactic one:
fun(obj, arguments)
as opposed to:
obj.met(arguments)
but the effect is just about the same."
?
bye
My suggestion
- OO programming:
length(foo) calls the foo instance(or class for some language) length method
length(bar) calls the bar instance length method, it is **not** the same
than the foo method
- non OO programming:
length(foo) calls the length function with foo as parameter
length(bar) calls the **same** length function with the bar parameter
(so you better make sure length is handling both foo and bar)
So the difference is definitely **not** a syntactic one, but it is true
that the syntax obj.method() better illustrates the bind mechanism of
the OO programming.
JM
In Python, the difference would be that functions don't automatically
dispatch (you have a function for everybody, and any dispatch you
perform based on argument types or other qualifiers has to be done
manually) whereas methods do dispatch on the first [implied] argument
(you execute a precise method corresponding to the object it's being
called on).
So fun(obj, *arguments) will call the same `fun` whether `obj` is an
int, a string or an HTTPFactory whereas obj.met(*arguments) will call
a different `met` each time (they all have the same "unqualified" name
[and signature if it's part of a protocol], but might very well be
implemented completely differently). Therefore -- in python -- the
effect isn't anywhere "just about the same".
> 1.) No need to use "()" to call a function with no arguments.
> Python --> "obj.m2().m3()" --ugly
> Ruby --> "obj.m1.m2.m3" -- sweeet!
> Man, i must admit i really like this, and your code will look so much
> cleaner.
>
+1
> 2.) the .each method
> container.each{|localVar| block}
> This method can really cleanup some ugly for loops, although i really
> like the readability of for loops.
>
Not clear.
> 3.) true OOP
> Now before you go and get all "huffy" over this statement, hear me
> out. Python is the best language in the world. But it damn sure has
> some warts! "len(this)" instead of "obj.length" max(that) instead of
> [1,2,3,4,5].max(). You know what i am talking about here people. We
> all get complacent and It seems easier to just cope with these
> problems instead of fighting for change. But look at the French, WHAT
> THE HELL HAS THAT DONE FOR THEM, *NOTHING*!!!!
>
+0.6
or better [1, 2, 3, 4, 5].len or [1, 2, 3, 4, 5].max
What has this got to do with "true OOP"?
[snip]
> 1.) No need to use "()" to call a function with no arguments.
> Python --> "obj.m2().m3()" --ugly
> Ruby --> "obj.m1.m2.m3" -- sweeet!
> Man, i must admit i really like this, and your code will look so much
> cleaner.
I personally would not prefer this, and would likely continue to use
(), precisely for code clarity - let me explain:
foo.nextval
foo.val
foo.previousval
Which of the calls above referenced instance variables, and which ones
called functions which changed the internal state of foo? I would have
trouble saying, just based on the calls above. I would have to go back
to the definition or documentation of foo to identify which is doing
what. On the other hand, the following gives a better clue as to what
is happening (granted not perfect, but better):
foo.nextval()
foo.val
foo.previousval()
~G
I agree with alex here. Will take the explicit syntax over the extra
cognitive load of figuring out exactly what's going on with
`obj.m1.m2.m3`.
Python has its warts, but requiring ()'s on function calls isn't one
of them. :)
I held your exact same view before i learned the Ruby language. And
your veiw makes some good points, however, naming conventions with
eliminate this problem all together. All "method names" should use the
underscore to separate words, "variable names" should use camelCase,
"constants" in all caps, and "class defs" in titlecase.
def go_and_do_this_for_me_now(self, *args)
self.variableNameHere
MyClassNameHere
THISISACONSTANT -or- THIS_IS_A_CONSTANT
in your example i would have used the following
foo.next_value
foo.value
foo.prev_value
good naming conventions will make your life (and everybody else's)
much easier when debugging code.
> and which ones called functions which changed the internal state of
> foo?
That you can't say, but neither can you say in Python as they could
all be properties. And of course just because it's a method doesn't
mean it mutates the object.
> I would have
> trouble saying, just based on the calls above. I would have to go back
> to the definition or documentation of foo to identify which is doing
> what. On the other hand, the following gives a better clue as to what
> is happening (granted not perfect, but better):
Well... it doesn't give much of a clue no really.
Furthermore Ruby has a pretty nice convention (sadly not used enough I
think) taken from Scheme where it's possible to postfix a method name
with "!" (note: the "!" is part of the name, there's no magic) to
indicate that this method modifies the object it's called on rather
than simply returning stuff.
Another oddity i did not like at first but must admit is growing on me
vector.reverse --> returns a new reversed vector
vector.reverse! --> modifies the instance vector in-place
Of course in python you would do...
vector.reverse --> in-place
vector.reversed --> in-place
The above example works pretty good, but this doesn't always "sound"
good. Take for example this...
point3d.offset --> return a new pt
point3d.offseted --> What did you say!?!?!
Now try Ruby's way
point3d.offset!
point3d.offset
a symbol works better for these things
Of course in python you would do...
vector.reverse --> in-place
vector.reversed --> new vector
Just feeding the troll.
JM
You can still use empty parenthesis in Ruby if that floats your boat.
obj.method() <-- note the empty parenthesis
I am just saying don't make them mandatory, thats all. In any case
where ambiguity abounds, by all means use the damn parenthisis.
Now Ruby will even let you call a method *that has arguments* with no
parenthisis -- however, i do not think this is a good idea. Consider
the following
Geom::Transformation.rotation pt, vec, ang
bad coder!, bad!
...and leads to worn out eyeball parsers in a hurry
Geom::Transformation.rotation(pt, vec, ang)
yes, good boy!
You do know that only one of those works in-place, right?
> The above example works pretty good, but this doesn't always "sound"
> good. Take for example this...
> point3d.offset --> return a new pt
> point3d.offseted --> What did you say!?!?!
>
> Now try Ruby's way
> point3d.offset!
> point3d.offset
>
> a symbol works better for these things
Or you could use a better verb:
point3d.move -> modifies in place
point3d.moved -> returns a new point
-Carsten
That's true. FOCAL scarred me for life.
> but i now realize Ruby has some good
> things going for it.
Any language that gets any sort of real use has to have. For instance,
I love Ada's numeric types (you can specify either the minimum number
of significant figures or the maximum delta for a real type, and it
will give you a type that satisfies that -- or a compilation error if
it can't. That matches genuine problem domains far better than having
to remember how many bits in a double on this particular system, and
reduces portability bugs).
> 3.) true OOP
> Now before you go and get all "huffy" over this statement, hear me
> out. Python is the best language in the world. But it damn sure has
> some warts! "len(this)" instead of "obj.length" max(that) instead of
> [1,2,3,4,5].max(). You know what i am talking about here people. We
> all get complacent and It seems easier to just cope with these
> problems instead of fighting for change. But look at the French, WHAT
> THE HELL HAS THAT DONE FOR THEM, *NOTHING*!!!!
I seem to recall recent studies showing that the French were on
average happier than Brits or Americans. Don't knock it!
> As for the rest of Ruby, i am not impressed. The redundant usage of
> "end" over indention perplexes me. The Perlish feel of "require" and
> the horrifically cryptic idioms of Ruby regular expressions. The
> "puts" and "gets" seem childish and the math class does not even have
> a degrees or radians function!
The operating system dependency built into the language did it for me.
That and the fact that I couldn't stop laughing for long enough to
learn any more when I read in the Pragmatic Programmer's Guide that
"Ruby, unlike less flexible languages, lets you alter the value of a
constant." Yep, as they say "Bug" = "Undocumented feature"!
--
Tim Rowe
how to reverse a string in python? must i usa a temp? like:
>>> s = "ciccio"
>>> l = list(s)
>>> l.reverse()
>>> s = "".join(l)
>>> s
'oiccic'
>>>
???
bye
That doesn't sound like a feature I would appreciate much, though I am
a big fan of "we're all adults here" programming. Though if they are
all functions, then adding parentheses wouldn't make much difference,
so offering the option of getting rid of them makes sense.
>> I would have
>> trouble saying, just based on the calls above. I would have to go back
>> to the definition or documentation of foo to identify which is doing
>> what. On the other hand, the following gives a better clue as to what
>> is happening (granted not perfect, but better):
> Well... it doesn't give much of a clue no really.
It, with few exceptions, tells me that fetching the value foo.val will
not modify the state of foo. Important stuff, at least to me.
doesn't the decimal module do some of that too?
bye
>>> s = "ciccio"
>>> s[::-1]
'oiccic'
s = s[::-1]
>>> I would have
>>> trouble saying, just based on the calls above. I would have to go
>>> back
>>> to the definition or documentation of foo to identify which is doing
>>> what. On the other hand, the following gives a better clue as to
>>> what
>>> is happening (granted not perfect, but better):
>
>> Well... it doesn't give much of a clue no really.
>
> It, with few exceptions, tells me that fetching the value foo.val will
> not modify the state of foo. Important stuff, at least to me.
But because of those few exceptions, it really tells you that foo.val
*probably* doesn't modify the state of foo. Unless you go source-
diving, you can't know for sure. Only Haskell gives you that kind of
guarantees (and even then, the code you call could have an unsafe*
somewhere)
DaveA
lol
bye
> On 30 Jul 2009, at 06:04 , alex23 wrote:
>> On Jul 30, 1:06 pm, r <rt8...@gmail.com> wrote:
>>> 2.) the .each method
>>> container.each{|localVar| block}
>>> This method can really cleanup some ugly for loops, although i really
>>> like the readability of for loops.
>>
>> map(lambda localVar: <block>, sequence)
>>
>> or:
>>
>> def usefully_named_func(var):
>> <block>
>> return var
>>
>> transformed = [usefully_named_func(v) for v in sequence]
>>
> The issue here is of course that `map` and comprehensions are
> transformations. `#each` exists for effectful iterations (Ruby has
> `#map` for the map operation). So the intent expressed by `#each` and
> `map` isn't the same. Furthermore and this is the most problematic
> limitation of Python here, `lambda` doesn't allow complex
> transformations due to its restrictions, so one has to switch to named
> functions which works but isn't sexy (and tends to lower readability
> imo).
I don't see any real limitation. What's wrong in:
for localVar in container:
block
And ruby's container.each is very similar to Python's iter()
*j
--
Jan Kaliszewski (zuo) <z...@chopin.edu.pl>
> And ruby's container.each is very similar to Python's iter()
>
Uh… not at all…
> how to reverse a string in python? must i usa a temp? like:
>
> >>> s = "ciccio"
> >>> l = list(s)
> >>> l.reverse()
> >>> s = "".join(l)
> >>> s
> 'oiccic'
> >>>
>
> ???
No.
>>> ''.join(list(reversed('abc')))
'cba'
>>> 'abc'[2::-1]
'cba'
>>> 'abc'[1000000000::-1]
'cba'
Any int >= len(string)-1 will do, so the call to len will usually not be
necessary.
Needing strings reversed is not common. More common might be
for char in reversed(somestring): process(char)
Terry Jan Reedy
No "r" never wrote anything like that. reversing a string is RTFM
material, this is basic stuff here! Stop quoting me as saying things i
did not say! I never asked how to reverse a string, "superpollo"
did.
It has benefits - code does look better. It has also significant cons - it is
ambiguous.
For instance:
a = b
Is b a variable or a method called without parameter?
> 2.) the .each method
> container.each{|localVar| block}
> This method can really cleanup some ugly for loops, although i really
> like the readability of for loops.
It's not only the 'each' method. It's "anonymous callbacks everywhere", which
is pretty cool. Certainly one of my favourite features of Ruby.
> 3.) true OOP
> Now before you go and get all "huffy" over this statement, hear me
> out. Python is the best language in the world. But it damn sure has
> some warts! "len(this)" instead of "obj.length" max(that) instead of
> [1,2,3,4,5].max(). You know what i am talking about here people. We
> all get complacent and It seems easier to just cope with these
> problems instead of fighting for change.
While ob.length() is more clear to read, it follows the same "operator" logic
as __iter__. Which is OK, I suppose.
> But look at the French, WHAT
> THE HELL HAS THAT DONE FOR THEM, *NOTHING*!!!!
I assume this was supposed to be funny?
> As for the rest of Ruby, i am not impressed. The redundant usage of
> "end" over indention perplexes me.
You have the choice to use {}. If you are confused over languages which do not
use indentation to delimit code blocks, you should probably try out a few
languages outside Python.
> The Perlish feel of "require" and
> the horrifically cryptic idioms of Ruby regular expressions.
Cryptic idioms of Ruby regular expressions? They are Perl-like regular
expressions, not exactly a far cry from what re does. Also, you can chain
them, which is extremely nice.
> The "puts" and "gets" seem childish
Well, they're short and easy. Writing "gets" is more intuitive than using
raw_input()
> and the math class does not even have
> a degrees or radians function!
>
> Anyway, i thought i would get this off my chest and catch up with old
> friends in the meantime. We can all make Python the perfect language
> it needs to be, but it ain't gonna be easy!
> Thank you all
>
> PS stay tuned for more from this series....
Cheers,
Emm
Hello Emanuel,
Again, who so ever names a method with such a non-descriptive name
will get whats coming to him. And if you did for some reason use such
a cryptic name as "b", do yourself (and everyone else) a favor and
follow it with "()" to denote the method call. Remember when something
is optional that means you have an option to use it OR not use it.
optional (http://definr.com/optional)
adj: possible but not necessary; left to personal choice [ant:
obligatory]
simple solutions for simple problems.
> On 30 Jul 2009, at 22:23 , Jan Kaliszewski wrote:
>> 30-07-2009 o 13:36:49 Masklinn <mask...@masklinn.net> wrote:
>>
>>> On 30 Jul 2009, at 06:04 , alex23 wrote:
>>>> On Jul 30, 1:06 pm, r <rt8...@gmail.com> wrote:
>>>>> 2.) the .each method
>>>>> container.each{|localVar| block}
>>>>> This method can really cleanup some ugly for loops, although i really
>>>>> like the readability of for loops.
>>>>
>>>> map(lambda localVar: <block>, sequence)
>>>>
>>>> or:
>>>>
>>>> def usefully_named_func(var):
>>>> <block>
>>>> return var
>>>>
>>>> transformed = [usefully_named_func(v) for v in sequence]
>>>>
>>> The issue here is of course that `map` and comprehensions are
>>> transformations. `#each` exists for effectful iterations (Ruby has
>>> `#map` for the map operation). So the intent expressed by `#each` and
>>> `map` isn't the same. Furthermore and this is the most problematic
>>> limitation of Python here, `lambda` doesn't allow complex
>>> transformations due to its restrictions, so one has to switch to named
>>> functions which works but isn't sexy (and tends to lower readability
>>> imo).
>>
>> I don't see any real limitation. What's wrong in:
>>
>> for localVar in container:
>> block
>>
> Well what's wrong with using that rather than `map`, `filter` or a list
> comprehension? (and if you don't see what the limitations of `lambda`
> are, you probably very rarely use it)
I know well about the expression-only-limitation of lambda, fortnately
there is the 'for' loop construct (or, alternatively, you can define
a named function).
And then, what's wrong with using 'for' rather than 'map', 'filter' etc.?
Agree, that 'anonymous block syntax' would be nice in some cases, but
I don't see any real limitation caused by lack of it i.e. something you
can't (all you can only with unreasonable effort).
>> And ruby's container.each is very similar to Python's iter()
> Uh… not at all…
OK, .each is like Python's iter() + some form of iterating over it
('for' loop or '[i]map'...).
Still, in this matter there is no real functionality in Ruby that you
can't reach in Python.
All that confessions of a (former?) Python fanboy are about sintactic
sugars, which importance is incomparable which such issues as e.g.
particular mechanism of classes, their inheritance etc. or even such
small helpers like function annotations.
Cheers,
As I understood the question, it was "was wrong in 'for var in container' in
comparison with ruby's container.each?"
What's the (semantic) difference between
for localVar in container:
block
and
container.each{|localVar| block}
?
(the sintactic difference is quite clear... if there is no semantic
difference, I'd rather use the first version!)
Map, filter and comprehensions have little to do with that (syntactic sugar
that may allow some optimizations, but that could otherwise be replaced by
the for loop).
> (and if you don't see what the limitations of
> `lambda` are, you probably very rarely use it)
I'm not the poster, but:
* I use lambdas, a lot.
* I "know" python lambdas are limited, compared with theoretical lambdas.
* I don't [usually] see that as a "limitation".
Most of the time, the limitation is the lack of multiline/multiexpression
lambdas. But I've never seen a case where I would /prefer/ to have a
multiline function definition inside a comprehension or method invocation,
instead of a named function defined two lines before it.
(The second famous limitation is the inability to use statements inside
lambdas... luckily, now that we have a ternary operator and a print function,
it is less limiting)
> > And ruby's container.each is very similar to Python's iter()
>
> Uh… not at all…
I agree with this. It should have said that they are equivalent in the sense
that they represent the iterator protocols of both languages, and nothing
more.
I'd like to ask, what "container.each" is, exactly? It looks like a function
call (as I've learned a few posts ago), but, what are its arguments? How the
looping "works"? Does it receive a "code" object that it has to execute?
Is .each some kind of magic keyword? (This has little to do with python or
the current thread, so feel free to reply off-list if you want to...)
Regards,
Luis.
--
Luis Zarrabeitia (aka Kyrie)
Fac. de Matemática y Computación, UH.
http://profesores.matcom.uh.cu/~kyrie
Hello Luis,
I think your question is completely valid within the context of this
thread. The purpose of his thread was to get feedback on how Python
and Ruby ideas could be cumulated into the best high level language.
And being that i am the BDFL of the "Confessions of a Python Fanboy"
thread, you have my personal permission to continue on with this
subject matter...,
I believe his point is that it is ambiguous to the compiler, not humans reading
the code. Python functions and methods are first class objects. They can be
passed around. If they were auto-called, then you could not do this.
--
Robert Kern
"I have come to believe that the whole world is an enigma, a harmless enigma
that is made terrible by our own mad attempt to interpret it as though it had
an underlying truth."
-- Umberto Eco
>> Any language that gets any sort of real use has to have. For instance,
>> I love Ada's numeric types (you can specify either the minimum number
>> of significant figures or the maximum delta for a real type, and it
>> will give you a type that satisfies that -- or a compilation error if
>> it can't. That matches genuine problem domains far better
>
> doesn't the decimal module do some of that too?
Not as far as I can tell, no.
--
Tim Rowe
Nor did what I posted say that you did. The above says that superpollo
wrote "how to reverse..., as he did. Quotes are indented once more than
the author. The above says 'r wrote' with no indent and then "No...",
which you did write, indented once.
I agree that since I did not quote anything that you wrote, I should
also have snipped your name. But I least I do attempt to snip material
irrelavant to my comments.
Terry Jan Reedy
I don't think "each" is a particularly compelling example of Ruby's
blocks - as you say, it's easily replaceable with a Python for-loop. The
advantage of Ruby's syntax is that it allows you to define further
functions that are sort-of like new control structures. So you have
things like:
File.open('myfile', 'r') do |file|
while line = file.gets
puts line
end
end
Which is like a python's:
with open('myfile', 'r) as f:
for line in f:
print f
However, with Ruby, this kind of scoping construct can be added without
adding new syntax. Another interesting example is the Sinatra web
framework, which allows you to write a web app with something like:
get '/' do
"Index page"
end
post '/:name' do
"Posted to " + params[:name]
end
Which looks something like a DSL for web apps, but is accomplished solely
with normal ruby functions. More: http://www.sinatrarb.com/intro.html
Ruby blocks are, fundamentally, syntactic sugar, but they are kind of
neat.
Actually, both. Use a variable you didn't initialize? This is what you get:
NameError: undefined local variable or method `b' for main:Object
from (irb):1
The compiler has no idea if b is a variable or a method. It also makes it very
easy to shadow an existing method by declaring a variable of the same name,
which is problematic. I suppose it is a side-effect of Ruby's Perl
philosophical inheritance - except that Perl uses sigils to prefix its
variables, negating this issue.
Cheers,
Emm
> On 2009-07-30 16:44, r wrote:
>> On Jul 30, 4:29 pm, Emmanuel Surleau<emmanuel.surl...@gmail.com> wrote:
>>>> 1.) No need to use "()" to call a function with no arguments. Python
>>>> --> "obj.m2().m3()" --ugly
>>>> Ruby --> "obj.m1.m2.m3" -- sweeet!
>>>> Man, i must admit i really like this, and your code will look so much
>>>> cleaner.
>>> It has benefits - code does look better. It has also significant cons
>>> - it is ambiguous.
>>> For instance:
>>>
>>> a = b
>>>
>>> Is b a variable or a method called without parameter?
>>
>> Hello Emanuel,
>> Again, who so ever names a method with such a non-descriptive name will
>> get whats coming to him. And if you did for some reason use such a
>> cryptic name as "b", do yourself (and everyone else) a favor and follow
>> it with "()" to denote the method call. Remember when something is
>> optional that means you have an option to use it OR not use it.
>
> I believe his point is that it is ambiguous to the compiler, not humans
> reading the code. Python functions and methods are first class objects.
> They can be passed around. If they were auto-called, then you could not
> do this.
Oh my, "r" is still around is he??? And now he's singing the praises of
Ruby, the language which he treated as the Devil's Spawn when he first
arrived. That's hilarious.
But back on topic... "r" has missed the point. It's not that a=b is hard
to understand because b is a poor name. The example could have been:
def factory_function():
magic = time.time() # or whatever
def inner():
return magic
return inner
my_function = factory_function
It's still ambiguous. Does the programmer intend my_function to become
factory_function itself, or the output of factory_function?
In Python, it's not ambiguous at all -- my_function is set to
factory_function, and *not* the output of factory_function, because you
haven't called the function.
Python's model is consistent and simple: given a function "func", you
*always* refer to the function object itself as func and you *always*
call it with func(). This applies no matter how many arguments the
function takes, or what it returns, or where it is defined.
I think it's telling that "r" the fanboy has rejected Python's advantages
(simplicity, consistency, unambiguity) in favour of lazily saving two
keystrokes.
--
Steven
But I do think that using higher-order functions:
* tends to be terser while not going overly terse, it only removes
boilerplate
* is more composable (it's pretty easy to tack another transformer in
your chain, the same way defining iterator/generator transformers and
chaining them is simpler than doing the same thing using explicit
`for…in`)
* it clearly spells the intent of the programmer, while `for` can be
anything and everything, and one has to dive into the code to know
even the high-level operations performed (is it a basic
transformation? A filtering or partitioning? A reduction? An
application of side effects?)
> Agree, that 'anonymous block syntax' would be nice in some cases, but
> I don't see any real limitation caused by lack of it i.e. something
> you
> can't (all you can only with unreasonable effort).
>
There are no limitations, but once again there never are. There are no
actual limitations to using conditional jumps over iterators either.
>>> And ruby's container.each is very similar to Python's iter()
>
>> Uh… not at all…
>
> OK, .each is like Python's iter() + some form of iterating over it
> ('for' loop or '[i]map'...).
Well of course Enumerable#each is similar to map/imap, it's the same
core principle.
#each is simply a method that takes a function (called blocks in
ruby). One could call it a higher-order method I guess.
It's an implementation of the concept of internal iteration: instead
of collections yielding iterator objects, and programmers using those
through specially-built iteration constructs (e.g. `for…in`),
collections control iteration over themselves (the iteration is
performed "inside" the collection, thus the "internal" part) and the
programmer provides the operations to perform at each iterative step
through (usually) a function.
In Python (assuming we had anonymous defs and an each method on
lists), the following loop:
for item in some_list:
do_something(item)
do_something_else(item)
some_list.each((def (item):
do_something(item)
do_something_else(item)
))
There's absolutely nothing magic there, it's simply using anonymous
functions and method calls (SmallTalk initiated this approach in the
70s, and actually went much, much further than Ruby as it did away not
only with `for…in` but also with `if…else` and `while` and a bunch of
other stuff).
Now as IV pointed out, #each isn't the most interesting usage of
blocks/anonymous functions (though I do like it, because it gets rid
of no less than two keywords… even if Ruby reintroduced them as
syntactic sugar), higher-order functions (functions which act on other
functions) are (among other things) ways to create new control
structures without having to extend the core language (so are lisp-
style macros, by the way).
Of course Python does have higher-order functions (functions are first-
class objects, so it's possible and frequent to have functions act on
other functions), but since Python doesn't have anonymous functions
that usage tends to be a bit too verbose beyond simple cases (also, of
course, this kind of usages is neither in the "genes" nor in the
stdlib).
In closing, IV has an example of how blocks make `with` unnecessary in
Ruby (the functionality can be implemented at the library level rather
than the language one). Generally, anonymous functions in OO languages
are a way to inject behavior into third-party methods/objects. Kind-of
a first-class Visitor support.
-m
PS: there's actually a bit of syntactic magic in Ruby's blocks, and
it's one of the things I hate in the language, but it's not relevant
to the role of blocks, and unnecessary to it: SmallTalk has no magical
syntax).
> That and the fact that I couldn't stop laughing for long enough to learn
> any more when I read in the Pragmatic Programmer's Guide that "Ruby,
> unlike less flexible languages, lets you alter the value of a constant."
> Yep, as they say "Bug" = "Undocumented feature"!
That's no different from Python's "constant by convention". We don't even
get a compiler warning!
On the other hand, we don't have to prefix names with @ and @@, and we
don't have the compiler trying to *guess* whether we're calling a
function or referring to a variable.
Somebody who knows more Ruby than me should try writing the Zen of Ruby.
Something like:
Line noise is beautiful.
Simplicity is for the simple.
Complicated just proves we're smart.
Readability only matters to the schmuck who has to maintain our code.
Special cases require breaking the rules.
In the face of ambiguity, try to guess. Go on, what could go wrong?
The more ways to do it, the better.
Although I'm sure Ruby has its good points. I'm not convinced anonymous
code blocks are one of them though.
--
Steven
I believe "full" anonymous functions was intended by the author.
lambdas are limited to a single expression. "Full" anonymous functions
would be allowed to contain multiple statements.
Cheers,
Chris
--
http://blog.rebertia.com
No, because it has a name, namely "goBig"; this obviously prevents it
from being "anonymous".
For comparison, note how the function in the following example is
never given a name, and is thus anonymous:
>>> (lambda x: x+5)(6)
11
Descartes's doubt method :
"The basic strategy of /Descartes/
<http://www.philosophypages.com/ph/desc.htm>'s method of doubt
<http://www.philosophypages.com/dy/d9.htm#doubt> is to defeat skepticism
<http://www.philosophypages.com/dy/s5.htm#skep> on its own ground. Begin
by doubting the truth of everything�not only the evidence
<http://www.philosophypages.com/dy/e9.htm#evid> of the senses and the
more extravagant cultural presuppositions, but even the fundamental
process of reasoning itself. If any particular truth about the world can
survive this extreme skeptical challenge, then it must be truly
indubitable and therefore a perfectly certain foundation for knowledge"
So let's make the method call parenthesis a "truly indubitable and
therefore a perfectly certain foundation".
Those who want to remove the parenthesis on python method calls raise
the hand !
JM
Not only that - does 'return inner' return the function inner or the
result of function inner?
How does ruby pass a function as an object?
Iain
To get a (bound) method object, you simply call `method` on an
instance e.g. `foo.method(:bar)` is equivalent to `foo.bar` in Python
(it returns a bound method object without calling it).
Blocks are usually created as part of the calls: `foo.bar
{do_something}` the part between braces (braces included) is a block,
which will be passed to the method `bar`. Sadly, Ruby's blocks are
magical syntax (doesn't mean they have to be, in Smalltalk there's
nothing magical about blocks for instance) so you can't just do `foo =
{do_something}`, you have to turn them into `Proc` objects with the
`proc` constructor (or `lambda`, it's equivalent and looks better so
I'll use that): `foo = lambda {do_something}`. If you use the magical
syntax previously shown, Ruby handles the turning of a block into an
actual `Proc` instance.
And since Ruby doesn't have a `()` operator, it uses a method instead
(`#call`), so you simply do `foo.call` to execute the proc and get its
value.
All in all, much like Smalltalk, Ruby tends not to favor raw functions
the way Python does, so a direct translation of the Python code
doesn't make much sense.
Well, at least Python doesn't pretend to have real symbolic constants -
we all know it's only a convention !-)
> We don't even
> get a compiler warning!
Of course - from the compiler's POV, it's only usual rebinding.
> On the other hand, we don't have to prefix names with @ and @@,
Nope, we have to prefix them with 'self' or 'cls' (or even
'self.__class__').
> and we
> don't have the compiler trying to *guess* whether we're calling a
> function or referring to a variable.
Please re-read a bit more carefully - it's *all* method call. Python is
'uniform' in obj.name is always an attribute lookup (methods being
attributes), Ruby is uniform in that 'obj.name' is always a method call.
> Somebody who knows more Ruby than me should try writing the Zen of Ruby.
> Something like:
(snip childish parody of Python Zen)
Steven, is that any useful ?
>
> Although I'm sure Ruby has its good points. I'm not convinced anonymous
> code blocks are one of them though.
Ruby's code blocks come from Smalltalk, where they are an absolute
necessity since message passing (which code blocks are part of) is the
*only* builtin control flow in Smalltalk - so you just *need* this
construction to provide branching and iterations.
Wether it makes sense to have code blocks in Ruby is another question
since Ruby does provide traditional control flow features, but then one
could question Python's "lambda" (hem...) too.
In other words, I see it the other way around.
> Wether it makes sense to have code blocks in Ruby is another
> question since Ruby does provide traditional control flow features
Well it does at least allow for the creation of new flow control
structures in library land when the existing ones aren't enough (e.g.
allows Ruby not to require the introduction of a `with` statement).
Though Ruby's blocks are nowhere near as flexible as Smalltalk's.
I'm not sure about the way you understand it !-)
> I'm pretty sure "traditional"
> flow control structures preceded Smalltalk by a pair of decades
Yes, of course - and that's not the point. What's important is that:
> so it's
> not that Smalltalk's designers found it necessary to use blocks &
> messages, but that they understood blocks & messages could trivially
> replace most control structures (making *those* unnecessary), making the
> core language simpler and more flexible.
Exactly.
> In other words, I see it the other way around.
My wording may have been a bit confusing, indeed. It was implied (but
perhaps a bit unclear) that restricting control structures to messages
and blocks was a design choice.
>> Wether it makes sense to have code blocks in Ruby is another question
>> since Ruby does provide traditional control flow features
>
> Well it does at least allow for the creation of new flow control
> structures in library land when the existing ones aren't enough (e.g.
> allows Ruby not to require the introduction of a `with` statement).
Yeps. But then other "traditionnal" control flow features become redundant.
That's quite different, actually. Python doesn't claim to have
constants! Can't misinterpret what you can't have, can you?
[Holds breath while awaiting counter-example... :]
~Ethan~
The convention being detailed in PEP8: http://www.python.org/dev/peps/pep-0008/
basically, anything in ALL_CAPS is a constant, assuming you follow
those style guidelines.
Iain
Right, but that's a style guide and not a language definition. Nobody
claims that anything in there is a part of the language.
> Iain
> --
> http://mail.python.org/mailman/listinfo/python-list
>
>> On the other hand, we don't have to prefix names with @ and @@,
>
> Nope, we have to prefix them with 'self' or 'cls' (or even
> 'self.__class__').
Incorrect.
>>> class K:
... class_attribute = 'No @@ required.'
...
>>> K().class_attribute
'No @@ required.'
No 'self' or 'cls' in sight.
I can see a vague advantage to using @ to create instance attributes and
@@ to creating class attributes -- it makes it easy to create class
attributes inside a method without needing to do a double lookup. In
Python terms:
def K:
def method(self):
self.__class__.attr = "Class attribute"
would be:
def K:
def method(self):
@@attr = "Class attribute"
Advantages: you save a few characters and (possibly!) a couple of runtime
lookups.
Disadvantages: your code is filled with line noise. It's an arbitrary
choice between @@ meaning instance attribute and @@ meaning class
attribute -- there's no logical reason for choosing one over the other,
so you have to memorise which is which. It's easy to get it wrong.
'self' and 'cls' at least are words, even if 'cls' is badly misspelled :)
>> and we
>> don't have the compiler trying to *guess* whether we're calling a
>> function or referring to a variable.
>
> Please re-read a bit more carefully - it's *all* method call.
What did I misread from here?
[quote]
When Ruby sees a name such as ``a'' in an expression, it needs to
determine if it is a local variable reference or a call to a method with
no parameters. To decide which is the case, Ruby uses a heuristic. As
Ruby reads a source file, it keeps track of symbols that have been
assigned to. It assumes that these symbols are variables. When it
subsequently comes across a symbol that might be either a variable or a
method call, it checks to see if it has seen a prior assignment to that
symbol. If so, it treats the symbol as a variable; otherwise it treats it
as a method call.
[end quote]
And see the example "pathological case" comparing a function call (not
method) and a variable.
http://ruby-doc.org/docs/ProgrammingRuby/html/language.html
> Python is
> 'uniform' in obj.name is always an attribute lookup (methods being
> attributes), Ruby is uniform in that 'obj.name' is always a method call.
Which would be relevant if I was talking about method calls, but I wasn't.
>> Somebody who knows more Ruby than me should try writing the Zen of
>> Ruby. Something like:
>
> (snip childish parody of Python Zen)
>
> Steven, is that any useful ?
It made me feel good.
But seriously, while I admit that I have very little Ruby experience, and
so aren't in a great position to judge, it seems to me that Ruby doesn't
have anything like Python's over-riding design principles (the Zen). If
there is a design principle to Ruby, I can't see what it is.
I'm the first to admit that I'm far too inexperienced with the language
to make this a fair judgement. Unfair it might be, but it doesn't
necessarily mean I'm wrong! *wink*
Ruby just seems to be far more complicated than Python: things which
Python does at runtime, with a function call, Ruby has special syntax for:
"a bunch of words".split()
%w(a bunch of words)
ord('a')
?a
That makes the barrier to entry far higher: it's easier to leverage
existing knowledge to interpret unfamiliar Python code than unfamiliar
Ruby code. Or so it seems to me.
Oh, and I admit that Python decorators are a conspicuous counter-example.
I wouldn't do without them, but neither would I expect somebody to intuit
what they do.
>> Although I'm sure Ruby has its good points. I'm not convinced anonymous
>> code blocks are one of them though.
>
> Ruby's code blocks come from Smalltalk, where they are an absolute
> necessity since message passing (which code blocks are part of) is the
> *only* builtin control flow in Smalltalk - so you just *need* this
> construction to provide branching and iterations.
Just because Smalltalk had a particular (mis?)feature doesn't mean that
other languages should copy it. I know, I know, Ruby people swear by
anonymous code blocks, and I've read Paul Graham too. But I'm really not
so sure that the benefits of anonymous code blocks are great enough to
overcome the disadvantages of anonymous code blocks.
> Wether it makes sense to have code blocks in Ruby is another question
> since Ruby does provide traditional control flow features, but then one
> could question Python's "lambda" (hem...) too.
lambda, by allowing the function to be only a single expression, doesn't
suffer the disadvantages of anonymous code blocks. lambda, by allowing
the function to be only a single expression, also has fewer advantages
than anonymous code blocks. I think lambda ends up on the "more
advantages than disadvantages" side. I'm keeping my mind open regarding
Ruby code blocks.
--
Steven
> Ruby just seems to be far more complicated than Python: things which
> Python does at runtime, with a function call, Ruby has special
> syntax for:
>
> "a bunch of words".split()
> %w(a bunch of words)
>
> ord('a')
> ?a
>
That's the Perl heritage. And since it inherits from Perl, TIMTOWTDI:
>> "a bunch of words".split
=> ["a", "bunch", "of", "words"]
>> "a"[0]
=> 97
(yes, the indexing operator returns a character code if provided a
single index. With two, it returns as string slice)
Oh and %w() isn't really equivalent to split(): you don't use it to
split a string but to create a list of strings, so the equivalent
expression in Python would be `["a", "bunch", "of", "words"]`.
> That makes the barrier to entry far higher: it's easier to leverage
> existing knowledge to interpret unfamiliar Python code than unfamiliar
> Ruby code. Or so it seems to me.
>
That's probable.
>>> Although I'm sure Ruby has its good points. I'm not convinced
>>> anonymous
>>> code blocks are one of them though.
>>
>> Ruby's code blocks come from Smalltalk, where they are an absolute
>> necessity since message passing (which code blocks are part of) is
>> the
>> *only* builtin control flow in Smalltalk - so you just *need* this
>> construction to provide branching and iterations.
>
> Just because Smalltalk had a particular (mis?)feature doesn't mean
> that
> other languages should copy it. I know, I know, Ruby people swear by
> anonymous code blocks, and I've read Paul Graham too. But I'm really
> not
> so sure that the benefits of anonymous code blocks are great enough to
> overcome the disadvantages of anonymous code blocks.
>
What are the disadvantages of anonymous functions?
> #each is simply a method that takes a function (called blocks in ruby).
> One could call it a higher-order method I guess.
>
> It's an implementation of the concept of internal iteration: instead of
> collections yielding iterator objects, and programmers using those
> through specially-built iteration constructs (e.g. `for…in`),
> collections control iteration over themselves (the iteration is
> performed "inside" the collection, thus the "internal" part) and the
> programmer provides the operations to perform at each iterative step
> through (usually) a function.
Python's iterator protocol was developed in part to avoid the
(inside-out) callback style of programming. Writing virtual collections
as generator functions instead of iterator or iterable classes saves a
lot of boilerplate code. The itertools modules shows how nicely
iterators can be composed in a way that is much more awkward with callbacks.
> In Python (assuming we had anonymous defs and an each method on lists),
> the following loop:
>
> for item in some_list:
> do_something(item)
> do_something_else(item)
>
> some_list.each((def (item):
> do_something(item)
> do_something_else(item)
> ))
And how does Ruby do the equivalent of
def double(it):
for i in it:
yield 2*i
for i,j in zip(double(some_list), some_gen_func(args)):
print(do_something(i+j,i-j))
Terry Jan Reedy
Perhaps you can't do lambda foo: print foo, but you *can* do lambda x:
sys.stdout.write(x).
Combined with the ternary if, it seem sufficient if you want to stick
to the ideal "Simple is better than Complex". Sure, with statements
allowed in anonymous functions, you can save on a line of typing (def
blargh(x):), but I don't feel the obfuscation is worth the cost.
~G
We don't actually *declare* that something is constant and then have
that declaration ignored. Python doesn't lie to us, although (as in
any language) a programmer might.
--
Tim Rowe
Somethign along the lines of
some_list.map{|e| 2*e}.zip(some_gen_func(args)).each {|i, j|
puts(do_something(i+j, i-j))
}
The `#each` call after `#zip` is not actually necessary, but I find it
clearer. Oh, and some_gen_func and do_something probably wouldn't work
that way (as I said above, Ruby isn't big on named functions and
doesn't actually have them)
> > I know, I know, Ruby people swear by
> > anonymous code blocks, and I've read Paul Graham too. But I'm really
> > not so sure that the benefits of anonymous code blocks are great
> > enough to overcome the disadvantages of anonymous code blocks.
> >
> What are the disadvantages of anonymous functions?
In no particular order, and not necessarily exhaustive:
* The risk of obfuscation in your code. That's fairly minimal for
lambdas, because they're just a single expression, but for a large
anonymous code block (ACB) defined inside a named function, it may be
difficult for the reader to easily distinguish which bits are the outer
function and which are the ACB.
* Loss of useful debugging information. Take this example from Python:
>>> def main(f):
... return f(3)
...
>>> main(lambda n: 2/(n-3))
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in main
File "<stdin>", line 1, in <lambda>
ZeroDivisionError: integer division or modulo by zero
>>>
>>> def my_special_function(n):
... return 2/(n-3)
...
>>> main(my_special_function)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in main
File "<stdin>", line 2, in my_special_function
ZeroDivisionError: integer division or modulo by zero
If your code has only one anonymous function (whether a lambda or a full
multi-line block), then it's easy to identify which lambda raised the
exception: there is only one it could be. But if your code uses lots of
lambdas, the lack of a function name makes it hard to distinguish one
<lambda> from another <lambda>. Anonymity makes identification harder.
* Risk of code-duplication and breaking the principle of Once And Only
Once. Anonymous functions are generally created, used, then immediately
thrown away -- or at least made more-or-less inaccessible for reuse. An
anonymous function stored in a callback still exists, but the coder isn't
able to easily re-use it for another callback somewhere else in the code.
Consequently, there's a temptation for the coder to write the same
function multiple times:
add_button("Parrot", colour=blue, callback=lambda x: x.stuff('a'))
add_button("Cheese", flavour=tasty, callback=lambda x: x.thing('b'))
add_button("Canary", colour=yellow, callback=lambda x: x.stuff('a'))
instead of:
def bird_callback(x):
return x.stuff('a')
add_button("Parrot", colour=blue, callback=bird_callback)
add_button("Cheese", flavour=tasty, callback=lambda x: x.thing('b'))
add_button("Canary", colour=yellow, callback=bird_callback)
* Recursion is more or less impossible without fragile tricks.
(At least for Python. I don't know how recursion operates in Ruby.)
--
Steven
You could say that Ruby doesn't either, you just need to read the
documentation. Ruby's unwritten motto is "flexibility über alles". In this
regard, it is consistent (1). Not much is really bolted down in Ruby. You get
encapsulation, but it's so easy to break that it's mostly symbolic. It's a
language which gives great power to the programmer. Whether Ruby programmers
handle this power responsibly is another debate.
Cheers,
Emm
(1) I find Ruby to be pretty consistent in general, which is not always the
case of Python.
The traceback gives you the line of the anonymous function (even in
python) so unless you have several anonymous functions on the same
line, there's no reason why that would be much of an issue.
Furthermore, Python doesn't provide any more information when the
error happens out of a function (in a `for` or a `with`), so it's not
like there's much of a difference here between Ruby's block-based
approach and Python's statements-based approach.
> * Risk of code-duplication and breaking the principle of Once And Only
> Once. Anonymous functions are generally created, used, then
> immediately
> thrown away -- or at least made more-or-less inaccessible for reuse.
> An
> anonymous function stored in a callback still exists, but the coder
> isn't
> able to easily re-use it for another callback somewhere else in the
> code.
> Consequently, there's a temptation for the coder to write the same
> function multiple times:
>
> add_button("Parrot", colour=blue, callback=lambda x: x.stuff('a'))
> add_button("Cheese", flavour=tasty, callback=lambda x: x.thing('b'))
> add_button("Canary", colour=yellow, callback=lambda x: x.stuff('a'))
>
> instead of:
>
> def bird_callback(x):
> return x.stuff('a')
>
> add_button("Parrot", colour=blue, callback=bird_callback)
> add_button("Cheese", flavour=tasty, callback=lambda x: x.thing('b'))
> add_button("Canary", colour=yellow, callback=bird_callback)
>
Yes, that one I can give you though I don't think that's a big issue.
And it's not like it's hard to extract the anonymous function into a
named one and then use that on the third strike, so I really don't
believe that point holds much water.
> * Recursion is more or less impossible without fragile tricks.
>
> (At least for Python. I don't know how recursion operates in Ruby.)
Code blocks are rarely if ever used recursively. If an operation is
using anonymous functions recursively, then there's often something
very wrong with the idea leading to that code. So I find this
objection irrelevant.
>> We don't actually *declare* that something is constant and then have
>> that declaration ignored. Python doesn't lie to us, although (as in any
>> language) a programmer might.
>
> You could say that Ruby doesn't either,
Well you could say a lot of things. Admittedly you don't need a separate
"constant" declaration, but creating a variable name with an initial
uppercase letter is sufficient to make it a (supposed) constant:
irb(main):049:0* Thing = 5
=> 5
irb(main):050:0> Thing = 7
(irb):50: warning: already initialized constant Thing
=> 7
As you can see, Ruby (well, irb at least) considers that Thing is a
constant, and then goes ahead and changes it anyway.
Apart from the use of an arbitrary naming convention instead of an
explicit "make this constant" declaration, and the feeble way Ruby
capitulates when you change it, I think having actual write-once
constants is actually a plus.
> you just need to read the
> documentation. Ruby's unwritten motto is "flexibility über alles". In
> this regard, it is consistent (1).
"It's consistent in its inconsistency" sort of thing perhaps?
> Not much is really bolted down in
> Ruby. You get encapsulation, but it's so easy to break that it's mostly
> symbolic.
Out of curiosity, can you read/write class and instance attributes from
outside the class without using a getter/setter?
--
Steven
*shrugs* I see it as a way to encourage (but not force) people to follow a
coding convention. Is this such a bad thing?
> irb(main):049:0* Thing = 5
> => 5
> irb(main):050:0> Thing = 7
> (irb):50: warning: already initialized constant Thing
> => 7
>
> As you can see, Ruby (well, irb at least) considers that Thing is a
> constant, and then goes ahead and changes it anyway.
I'm quite aware of how constants work in Ruby, yes :)
> Apart from the use of an arbitrary naming convention instead of an
> explicit "make this constant" declaration, and the feeble way Ruby
> capitulates when you change it, I think having actual write-once
> constants is actually a plus.
Nothing wrong with naming conventions. This encourages a uniform coding style,
something which Python could really use.
> > you just need to read the
> > documentation. Ruby's unwritten motto is "flexibility über alles". In
> > this regard, it is consistent (1).
>
> "It's consistent in its inconsistency" sort of thing perhaps?
No, just consistent.
> > Not much is really bolted down in
> > Ruby. You get encapsulation, but it's so easy to break that it's mostly
> > symbolic.
>
> Out of curiosity, can you read/write class and instance attributes from
> outside the class without using a getter/setter?
If you have an instance f class with a attribute @foo which doesn't have an
accessor, you could do:
f.instance_eval("@foo='bar'")
And yes, instance_eval is evil.
Cheers,
Emm
Correct for all relevant cases, except this one:
>>>> class K:
> ... class_attribute = 'No @@ required.'
> ...
>>>> K().class_attribute
> 'No @@ required.'
Once again: Ruby's attributes are *private*, so you can't access an
attribute (class or instance) from outside a method. IOW, the above
example is irrelevant.
(snip)
> Disadvantages: your code is filled with line noise. It's an arbitrary
> choice between @@ meaning instance attribute and @@ meaning class
> attribute -- there's no logical reason for choosing one over the other,
> so you have to memorise which is which. It's easy to get it wrong.
So far that's something I have no difficulty living with.
>>> and we
>>> don't have the compiler trying to *guess* whether we're calling a
>>> function or referring to a variable.
>> Please re-read a bit more carefully - it's *all* method call.
>
> What did I misread from here?
Nowhere - it's me that got it wrong here, sorry.
(snip)
>>> Somebody who knows more Ruby than me should try writing the Zen of
>>> Ruby. Something like:
>> (snip childish parody of Python Zen)
>>
>> Steven, is that any useful ?
>
> It made me feel good.
Why ???
You don't like Ruby ? Fine, don't use it. Period. I can't see the point
of all these pissing contests.
> But seriously, while I admit that I have very little Ruby experience, and
> so aren't in a great position to judge, it seems to me that Ruby doesn't
> have anything like Python's over-riding design principles (the Zen). If
> there is a design principle to Ruby, I can't see what it is.
Fullfill the tastes of Matz ?-)
(snip)
>>> Although I'm sure Ruby has its good points. I'm not convinced anonymous
>>> code blocks are one of them though.
>> Ruby's code blocks come from Smalltalk, where they are an absolute
>> necessity since message passing (which code blocks are part of) is the
>> *only* builtin control flow in Smalltalk - so you just *need* this
>> construction to provide branching and iterations.
>
> Just because Smalltalk had a particular (mis?)feature
You can drop the 'mis' part IMHO. The point of code blocks in Smalltalk
is that once you have something as powerful as the message+code blocks
combo, you just don't need any other 'special form' for control flow.
> doesn't mean that
> other languages should copy it.
Nope. But OTHO, Python is famous for all the features it copied from
other languages !-)
>> Disadvantages: your code is filled with line noise. It's an arbitrary
>> choice between @@ meaning instance attribute and @@ meaning class
>> attribute -- there's no logical reason for choosing one over the other,
>> so you have to memorise which is which. It's easy to get it wrong.
>
> So far that's something I have no difficulty living with.
I don't like arbitrary symbols. Most people don't -- that's why "line
noise" is unpopular. It's hard to read, hard to write, hard to maintain,
and hard to talk about. The more line-noise, the worse the language.
Of course, *ultimately* every symbol is arbitrary. There's no reason why
"2" should mean the integer two, or "list" should mean a sequence type,
but some symbols have such a long history, or have some other connection
(say, with human languages), that the arbitrariness is lost. For
instance, "+" is the obvious, non-arbitrary choice for the addition
operator in any programming language using Latin symbols, and probably
any programming language on Earth. (Not the *only* choice, but the
obvious one.)
I have a similar dislike for decorator syntax, because "@" ("at" in
English) has nothing to do with decorations. It's an arbitrary symbol.
One might argue that "$" would have been a more logical choice, because
we turn numerals into currency by decorating it with a $ sign. (At least
in the US, Canada, Australia, and a few other countries.) I use
decorators all the time, and they are a fantastic invention, but the
arbitrariness of the @ syntax is a negative. Oh well, one negative out of
a whole lot of positives isn't too bad.
At least I only have to deal with *one* such arbitrary symbol that needs
memorizing. There's no need to distinguish between @@function_decorator
and @class_decorator (or should it be the other way around?). Similarly,
Python's choice of syntax for attributes is consistent: object.attribute
works for everything, whether object is a class, an instance, a module,
and whether attribute is callable or not. You can even use it on ints,
provided you are clever about it:
>>> 42 .__class__
<type 'int'>
>>>> Somebody who knows more Ruby than me should try writing the Zen of
>>>> Ruby. Something like:
>>> (snip childish parody of Python Zen)
>>>
>>> Steven, is that any useful ?
>>
>> It made me feel good.
>
> Why ???
>
> You don't like Ruby ? Fine, don't use it. Period. I can't see the point
> of all these pissing contests.
Criticism of a language is a pissing contest?
Yeah, okay, I was a tad dismissive. I un-apologetically jump to strong
impressions about languages based on minimal use -- but I'm also willing
to change my mind. Ruby certainly looks to me like it has some nice
features. Syntax that looks like Perl isn't one of them though.
>> Just because Smalltalk had a particular (mis?)feature
>
> You can drop the 'mis' part IMHO. The point of code blocks in Smalltalk
> is that once you have something as powerful as the message+code blocks
> combo, you just don't need any other 'special form' for control flow.
Well, maybe, but remember, programming languages are only partly for
communication to the compiler. They also have the requirement to
communicate with human programmers as well, and that's even more
important, because
(1) humans spent a lot more time working with code than compilers do;
(2) human programmers charge much more money than compilers do;
(3) and you can modify the compiler to suit human needs much more easily
than you can modify programmers to suit the compiler's needs.
So I'd ask, does Smalltalk's message passing model match the way human
beings think? If not, then that's a good sign it might be a misfeature.
>> doesn't mean that
>> other languages should copy it.
>
> Nope. But OTHO, Python is famous for all the features it copied from
> other languages !-)
Absolutely! There's nothing wrong with copying *good* features :)
--
Steven
Neither do I - when there are too many at least. But I can certainly
live with a couple ones. Now the point wasn't about my personal tastes,
but about the fact that this particular pair of "arbitrary symbols" was
IMHO still usable - IOW, I wouldn't dismiss Ruby on this sole point.
> Most people don't -- that's why "line
> noise" is unpopular. It's hard to read, hard to write, hard to maintain,
> and hard to talk about. The more line-noise, the worse the language.
OTHO, too much verbosity is a pain too. Ever programmed in AppleScript ?
"set the attribute XXX of object YYY of collection ZZZ to SomeValue"...
Yuck. I bet you prefer "zzz[yyy].xxx = SomeValue" - which uses three
arbitrary symbols.
(snip)
> Oh well, one negative out of
> a whole lot of positives isn't too bad.
Indeed. You could perhaps learn a bit more about Ruby's positives if you
don't block on what you perceive (rightfully or not) as negative points ?-)
(snip)
>>>>> Somebody who knows more Ruby than me should try writing the Zen of
>>>>> Ruby. Something like:
>>>> (snip childish parody of Python Zen)
>>>>
>>>> Steven, is that any useful ?
>>> It made me feel good.
>> Why ???
>>
>> You don't like Ruby ? Fine, don't use it. Period. I can't see the point
>> of all these pissing contests.
>
> Criticism of a language is a pissing contest?
Not necessarily. But:
> Yeah, okay, I was a tad dismissive. I un-apologetically jump to strong
> impressions about languages based on minimal use
Possibly, yes.
> -- but I'm also willing
> to change my mind. Ruby certainly looks to me like it has some nice
> features. Syntax that looks like Perl isn't one of them though.
Not my cup of tea neither FWIW. But Ruby is nowhere near Perl in terms
of "line noise".
>
>>> Just because Smalltalk had a particular (mis?)feature
>> You can drop the 'mis' part IMHO. The point of code blocks in Smalltalk
>> is that once you have something as powerful as the message+code blocks
>> combo, you just don't need any other 'special form' for control flow.
>
> Well, maybe, but remember, programming languages are only partly for
> communication to the compiler. They also have the requirement to
> communicate with human programmers as well, and that's even more
> important, because
>
> (1) humans spent a lot more time working with code than compilers do;
>
> (2) human programmers charge much more money than compilers do;
>
> (3) and you can modify the compiler to suit human needs much more easily
> than you can modify programmers to suit the compiler's needs.
>
> So I'd ask, does Smalltalk's message passing model match the way human
> beings think?
Does all human beings think the same way ? And aren't human beings able
to learn new ways ?
Smalltalk's only control flow construct might seem a bit weird at first
when all you've been exposed to so far are more "traditional" special
constructs, but it's not hard to learn and is way more uniform and
flexible than having special constructs for each and any possible
situation.
One could ask if functional programming or OO "matches the way human
beings think". From experience, some of us just find FP and / or OO just
obvious, and some won't never get it.
FWIW, there are quite a few features and idioms in Python that I _now_
find readable and obvious, but that would have puzzled me ten years ago.
This reminds me of a shop where the CTO had forbidden using any OO
feature of the main language used there because "nobody would understand
it" (needless to say, I only stayed there a couple weeks...).
>
>>> doesn't mean that
>>> other languages should copy it.
>> Nope. But OTHO, Python is famous for all the features it copied from
>> other languages !-)
>
> Absolutely! There's nothing wrong with copying *good* features :)
Well... at least when they make sense and integrate smoothly into the
target language.
It's not "line noise" if it conveys information...
> Of course, *ultimately* every symbol is arbitrary. There's no reason why
> "2" should mean the integer two, or "list" should mean a sequence type,
> but some symbols have such a long history, or have some other connection
> (say, with human languages), that the arbitrariness is lost. For
> instance, "+" is the obvious, non-arbitrary choice for the addition
> operator in any programming language using Latin symbols, and probably
> any programming language on Earth. (Not the *only* choice, but the
> obvious one.)
>
> I have a similar dislike for decorator syntax, because "@" ("at" in
> English) has nothing to do with decorations. It's an arbitrary symbol.
> One might argue that "$" would have been a more logical choice, because
> we turn numerals into currency by decorating it with a $ sign. (At least
> in the US, Canada, Australia, and a few other countries.) I use
> decorators all the time, and they are a fantastic invention, but the
> arbitrariness of the @ syntax is a negative. Oh well, one negative out of
> a whole lot of positives isn't too bad.
You can think of "@" as describing something being "at" the instance or
the class. "$" is totally arbitrary to me 'cause I don't thnk of my code as
currency...
> At least I only have to deal with *one* such arbitrary symbol that needs
> memorizing. There's no need to distinguish between @@function_decorator
> and @class_decorator (or should it be the other way around?). Similarly,
> Python's choice of syntax for attributes is consistent: object.attribute
> works for everything, whether object is a class, an instance, a module,
> and whether attribute is callable or not. You can even use it on ints,
> provided you are clever about it:
You can think of "@" as being at an instance or "@@" to be more
emphatically (as the Japanese do in their language) integrated with a class,
being available to all instances... or you can simply understand that
instance vairables are more common than class variables so the shorter
notation is used for the more common case...
You want to talk about arbitrariness? Why is len() a function you pass
objects into while objects can have methods that describe themselves to you?
At least Ruby defines operators using the actual name of the operator
instead of you having to remember an arbitrary magic incantation that
corresponds to the operator...
class Test
attr_reader :data
def initialize(data)
@data = data
end
# This is the operator part...
def + right
Test.new @data + right.data
end
end
>>>>> Somebody who knows more Ruby than me should try writing the Zen of
>>>>> Ruby. Something like:
>>>> (snip childish parody of Python Zen)
>>>>
>>>> Steven, is that any useful ?
>>>
>>> It made me feel good.
>>
>> Why ???
>>
>> You don't like Ruby ? Fine, don't use it. Period. I can't see the point
>> of all these pissing contests.
>
> Criticism of a language is a pissing contest?
>
> Yeah, okay, I was a tad dismissive. I un-apologetically jump to strong
> impressions about languages based on minimal use -- but I'm also willing
> to change my mind. Ruby certainly looks to me like it has some nice
> features. Syntax that looks like Perl isn't one of them though.
Yeah, that would be the "pissing contest" part.
You could simply have gone to the Ruby newsgroup and posted some
criticisms to see what was behind those decisions. However, that would have
avoided the pissing contest and perhaps you wanted one...
>>> Just because Smalltalk had a particular (mis?)feature
>>
>> You can drop the 'mis' part IMHO. The point of code blocks in Smalltalk
>> is that once you have something as powerful as the message+code blocks
>> combo, you just don't need any other 'special form' for control flow.
>
> Well, maybe, but remember, programming languages are only partly for
> communication to the compiler. They also have the requirement to
> communicate with human programmers as well, and that's even more
> important, because
>
> (1) humans spent a lot more time working with code than compilers do;
>
> (2) human programmers charge much more money than compilers do;
>
> (3) and you can modify the compiler to suit human needs much more easily
> than you can modify programmers to suit the compiler's needs.
>
> So I'd ask, does Smalltalk's message passing model match the way human
> beings think? If not, then that's a good sign it might be a misfeature.
I think this is overstated. Not all humans think in exactly the same
way so it's very presumptuous. It also ignores our intellect. We can
change the way we think and we can use this versatility to our advantage.
We can increase our productivity in certain tasks by changing the way we
think to some other paradigm that better serves a given purpose...
>>> doesn't mean that
>>> other languages should copy it.
>>
>> Nope. But OTHO, Python is famous for all the features it copied from
>> other languages !-)
>
> Absolutely! There's nothing wrong with copying *good* features :)
The trick, of course, is figuring out which features are good!
Interesting. I know what internal iteration is, and I suspected it was along
these lines when I saw the syntax and that .each was a function and not a
keyword. But if it is internal iteration and the .each method is receiving an
anonymous function, I wonder what is the scope of the variables in that
function. In pseudo-python terms (using your example),
x = 5
some_list.each((def (item):
do_something(item, x)
x = do_something_else(item)
))
(or something like that). In python, the inner function would be invalid
(the 'x' is local). For that construct to be equivalent to a for loop, the
anonymous function shouldn't create a new scope. Is that what is happening?
(If it is, this would be a big difference between anonymous and non-anonymous
functions).
Anyway, this is OT. Thank you for your reply.
(ah, sorry for taking so long... I was out of town)
--
Luis Zarrabeitia (aka Kyrie)
Fac. de Matemática y Computación, UH.
http://profesores.matcom.uh.cu/~kyrie