Deciding between ruby and python

Hi --

> Perhaps some of us like to do our iteration explicitly. I don't think
> there is any syntactic overhead, and the for loop reads
> better. Counterexamples welcome.
>
> Obviously we can do #each too:
>
> def L(x):
> print x
> return x*2
>
> map(L, [1,2,3])
>
(reverse (map (lambda (x) (begin (display x) (* x 2)))
      (map (lambda (x) (+ x 2)) (list 1 2 3))))

[1,2,3].each {|x| x+2}.each {|x| print x;return x*2}.reverse

I think you want #map there. With each, the first block is
essentially a no-op. Also, return will break through all levels
(including method call):

  def m
    [1,2].each {|x| puts x; return}
  end
  m # => 1

or if you prefer

[1,2,3].each {|x| x+2}.each do |x|
  print x
  x*2
end.reverse

Again, with #each rather than #map, you're just throwing away the
results of the calculations (x+2 and x*2).

[...]

a = [1,2,3]
a = for x in a
  print x
         x + 2
     end.reverse

In ruby you don't need to choose between map and for if you want the
result list, you can do whichever pleases you the most.

for doesn't generate a result list though. What you've got here will
return [3,2,1], and all the x+2 calculations will be discarded (like
#each).

David

···

On Mon, 5 Apr 2004, Charles Comstock wrote:

--
David A. Black
dblack@wobblini.net

And there is an insurmountable limitation (I think):

chars = words = lines = 0
str.each do |line|
chars += line.chomp.length
words += line.split
lines += 1
end

I don’t believe [Python] can carry outside state into its
“substitute for blocks”.

Feature-by-feature comparisons are useful insofar as they help you
understand differences and similarities between the two languages
being compared. Consider English and German, for example. The former
has an “insurmountable” limitation of not having explicit case
markings for the nominative, genitive, accusative, and dative cases
the way German does.

Now, this may be a useful thing to know, but it’s useless as far as
comparing the overall expressiveness, naturalness, and ease of use of
these two languages.

As Jim Weirich already pointed out in ruby-talk 96368, closures are a
poor man’s objects. One way of making a function carry state in
Python is to implement it as a callable object:

$ cat poor_mans_closure.py 
#!/usr/bin/python

class Foo:
    "A poor man's closure"

    def __init__(self, state):
        self.__call__ = self._call
        self.state = state

    def _call(self, x, y):
        return x * self.state + y


foo1 = Foo(1)
foo2 = Foo(42)

print foo1(2,3)
print foo2(2,3)

$ ./poor_mans_closure.py 
5
87
···

On Monday 05 April 2004 11:08, Gavin Sinclair wrote:

rolo wrote:

Why cannot we have attribute access of the object from anywhere in the program by sennding a message @attribute

class Example
def initialize
@number = ‘1.0’
end
end

e = Example.new
print e.@number => should print 1.0

That’s not how it works.

In Ruby, all data members are private by default. They can be
accessed only through methods.

I don’t recognize your name offhand. If you are a nuby, you may
not be aware of these possibilities for the class:

 attr_reader :number     # Let outside world read this value
 attr_writer :number     #  "     "       "  set it
 attr_accessor :number   #  "     "       "  read or set it

In practice, I’ve never used attr_writer. It’s hard to imagine
something useful being write-only and not readable. (Insert
Perl wisecrack here.)

Hal

Would this work for you?

class Example
attr_reader :number
def initialize
@number = ‘1.0’
end
end

e = Example.new
puts e.number #=> will print 1.0

···

— rolo rohitlodha@hotwireindia.com wrote:

Why cannot we have attribute access of the object
from anywhere in the program by sennding a message
@attribute

class Example
def initialize
@number = ‘1.0’
end
end

e = Example.new
print e.@number => should print 1.0


Do you Yahoo!?
Yahoo! Small Business $15K Web Design Giveaway
http://promotions.yahoo.com/design_giveaway/

Python. And if the needs of your anonymous function exceed what lambda
provides, you can use what I call big L notation:

>> def L(matchobj):
>> return matchobj.group(0).upper()

>> res = re.sub('u|a', L, 'huijaahuijaa')

> That's a poor man's substitute.  Seriously.  It means you have to name
> everything you're doing, and pollute the function namespace *for no
> good reason*.  Yes, you can "do it", but it's ugly in comparison.

You don’t really pollute it much, because you can always use the same
name. The old name (and as such, the old definition too) is clobbered
by the new name. If you always use big L for this purpose,

Also, you can do the deffing inside functions too. So it’s not even
visible at module level.

> Can you provide an implementation in the same style in Python?  It's
> not that I don't believe Python can count characters, lines and words
> in a string.  But I don't believe it can carry outside state into its
> "substitute for blocks".

Yes. Python supports lexical closures. The only gotcha is that you
can’t ‘assign’ the names of free variables to point to new objects,
i.e. it has to be the same object, though it can be mutated. So you
can use e.g. length 1 array to contain the other object.

The following code:

···

def get_char_counter():
chars = [0]

def filepusher(f):
    chars[0] += len(open(f).read())
    return chars[0]

return filepusher

acc = get_char_counter()

print acc(“/bootsect.dos”)
print acc(“/bootsect.dos”)


Prints out:


201
402

> # Connection: the database connection is automatically released at
> # the end of the block, including if there is an exception therein.

try - finally blocks are used for this in Python.

> # Transaction support: any exception in this block will cause a
> # "ROLLBACK".  If all is fine, a "COMMIT" is performed at the end.

try:
stuff()
except:
rollback()
else:
commit()

But I admit that the ruby approach seems cleaner here. It’s probably
easier to use and the logic doesn’t clutter the code.

> I'd like to see the same program written in Python.  I don't know the
> Python database interface, but I expect it would need to have explicit
> exception handling.  There would also be no automatic releasing of
> resources.

Back when I did db programming, I didn’t think it was such a big
deal. It seems bigger on the paper, though.

> To prove me wrong, please present your best effort at translating the
> database programming example above into Python.  Of course, you may
> disagree with my frame of reference instead, but I think that's pretty
> good.

Yes, I do indeed disagree with your frame of reference. I just don’t
think this issue such big a deal. you might want to go googling for
more elaborate discussion - code blocks have indeed been suggested for
Python, they just didn’t happen. It seems to pop up every now and
then, so perhaps they will be in Python some day if they are deemed
valuable enough. In the meantime, I don’t care much - Try-finally is
good enough for me :-). I would go googling myself, but I don’t really
have the time. There’s work to do, and I’m ‘between ADSL operators’ at
home for this month.


Ville Vainio http://tinyurl.com/2prnb

gabriele renzi wrote:

… it yields pairs
for you to do what you want with.

yes, after it constructed one big array:)

Yes, he’s creating a hash (not an array), but that’s presumably just because he grabbed the code from a real application, where he wants to be able to re-use the values.

He could just as easily have written the following, which only holds two values at any given time, but can iterate over as many as you like …

class Fib
def initialize(fn = 1, fn_1 = 1)
@fn = 1
@fn_1 = 1
end

def each(limit = 1000, &block)
limit.times do
yield @fn

  @fn, @fn_1 = @fn_1, (@fn + @fn_1)
end

end
end

fib = Fib.new

for i in fib
puts i
end

gabriele renzi wrote:

guess why you can’t use a prime? because you can’t pause the yielding.
You’re forced to use the whole number of Prime number that are
actually.. infinite.
So, you discover that you can’t zip it cause zip calls to_ary.

Alas, that is a limitation of the internal iterator paradigm that Ruby
uses. Infinite lists and things like the “same fringe” problem (see
http://onestepback.org/articles/same_fringe/index.html) are all examples
of problems that need more power than internal iterators provide.
Fortunately, the these types of problems are less common that those
addressed by internal iterators.

Add when we need bigger guns in Ruby, they are available. The generator
package is now standard in Ruby, so lazy zips are fairly easy to write
when you need them.

I find it fascinating how the different choices in iteration methods
effect how language users perceive and use the language. Python starts
with external iterators and uses continuation-like generators to gain
back the convenience of writing internal iterators. Ruby starts with
internal iterators and uses continuations to gain back the flexibility
of external iterators. Different trade offs with different implications
for users. But in the end, approximately equivalent.

lazy generators allows you to use just one element at a time, so zip
can be used for any kind of enumerable.

I did check out the generator package before I wrote this, so I might as
well share some code. Here’s one way to write a lazy zip in Ruby.

require ‘generator’
require ‘mathn’

def lazy_zip(*args)
gens = args.collect { |en| Generator.new(en) }
Generator.new do |composite|
while gens.all? { |g| g.next? }
composite.yield gens.collect { |g| g.next }
end
end
end

primes = Prime.new
pzip = lazy_zip(primes, 1..4)
4.times { p pzip.next }

Will print …

[2, 1]
[3, 2]
[5, 3]
[7, 4]

···


– Jim Weirich jim@weirichhouse.org http://onestepback.org

“Beware of bugs in the above code; I have only proved it correct,
not tried it.” – Donald Knuth (in a memo to Peter van Emde Boas)

Nope.

yep
just look at you own output.

guess why you can’t use a prime? because you can’t pause the yielding.
You’re forced to use the whole number of Prime number that are
actually… infinite.
So, you discover that you can’t zip it cause zip calls to_ary.

It occurred to me that it would be useful to be able to trace all
messages sent to an object in order to figure out how the object is
used. So, for example, I would like to be able to see all messages
sent to baz when it’s zipped with foo like so:

 foo.zip(baz)

I found tracer.rb in the Ruby library, but it seems to have been
written for a different purpose. I’m thinking of something similar
to this:

#!/usr/bin/ruby

class Tracer < Array
    Tracer.new.methods.each do |mm|
        undef_method mm unless mm == "__send__"
    end

    def initialize(proxied_object, name)
        @proxied_object = proxied_object
        @name = name
    end

    def method_missing(sym, *args)
        puts "#{sym} called for #{name}"
        @proxied_object.method(sym).call(*args)
    end
end


a = Tracer.new([1,2,3,4,5], "a")
b = Tracer.new([6,7,8,9,10], "b")

a.zip(b)

It almost looks like it should work. Unfortunately, this script
causes my Ruby interpreter to segfault. (I’m using Ruby 1.8.0 that
ships with Fedora Core 1.)

So, I guess my two questions are:

(a) Has anyone written something like this already?
(b) What does the above segfault on me?

Thanks,
Vadim

P.S. I googled for defadvice Ruby - Google Search but
nothing jumped out at me.

···

On Wednesday 07 April 2004 02:09, gabriele renzi wrote:

I won’t speak on Matz’s behalf, but it’s important to remember that
when you call #to_str, then you are getting back a string. That
sounds obvious, but all this talk of duck typing might confuse someone
into believing that you can influence the original object after you’ve
called #to_str on it!

When a class implements #to_str, it should be careful to return a new
object (e.g. use #dup) so that the caller can’t damage the object
itself.

The problem we have understanding to_str is that it slipped into

Ruby (I think) between 1.6 and 1.8, and it’s not treated in any

books that I know of.

Gavin

···

On Monday, April 5, 2004, 3:09:19 PM, Dave wrote:

In article 1081124063.259819.9185.nullmailer@picachu.netlab.jp, Yukihiro Matsumoto wrote:

Hi,

In message “Re: deciding between ruby and python” >> on 04/04/05, Guillaume Marcais guslist@free.fr writes:

Just a as we are on the subject. Should Ojbect#to_s return self.to_str
if it is defined instead of the default value?

I encourage defining “to_str” along with string methods, i.e. by
defining “to_str”, I expect the object to be a duck quacks like
strings. In that sense, you have to think before defining “to_str”,
more than it seems, even though your proposal makes sense.

I’d really like to understand what you mean, here. In your first sentence,
did you mean to say “I expect the object to be a duck that quacks like
strings?” If so, what does it mean to quack like a string? If not, what were
you really trying to say?

Hi,

I’d really like to understand what you mean, here. In your first sentence,
did you mean to say “I expect the object to be a duck that quacks like
strings?” If so, what does it mean to quack like a string? If not, what were
you really trying to say?

Suppose you understand “duck typing”.

Defining “to_str” is a way to make an object to behave like string
argument. Some C defined built in methods require String structure
inside. This is against duck typing principle, but need to be so for
performance. Those methods call “to_str” before processing string
arguments, as work around. When you want to implement an object that
behave exactly like strings, you need to define “to_str” method that
returns string representation (i.e. one with String structure) along
with other methods in String class.

In summary, “to_str” should always be part of string method set (since
it’s a performance hack). Emphasizing “to_str” alone too much is not
encouraged.

						matz.
···

In message “Re: deciding between ruby and python” on 04/04/05, Dave Benjamin ramen@lackingtalent.com writes:

David A. Black wrote:

Hi –

Perhaps some of us like to do our iteration explicitly. I don’t think
there is any syntactic overhead, and the for loop reads
better. Counterexamples welcome.

Obviously we can do #each too:

def L(x):
print x
return x*2

map(L, [1,2,3])

(reverse (map (lambda (x) (begin (display x) (* x 2)))
(map (lambda (x) (+ x 2)) (list 1 2 3))))

[1,2,3].each {|x| x+2}.each {|x| print x;return x*2}.reverse

I think you want #map there. With each, the first block is
essentially a no-op. Also, return will break through all levels
(including method call):

Whoops, just goes to show what happens when I forget to test the code
before posting. I’d forgotten that caveat about each, the commentary on
map remains true, I find it much easier to read in this order then the
above functional style.

[1,2,3].map {|x| x+2}.map {|x| print x;x*2}.reverse

[…]

for doesn’t generate a result list though. What you’ve got here will
return [3,2,1], and all the x+2 calculations will be discarded (like
#each).

David

Hmm I had misunderstood that about each, I encountered it once, but
promptly forgot about it. Do we do this to increase efficiency?
Otherwise I can’t see a situation where you wouldn’t want to return the
results of each for iteration.

Charles Comstock

···

On Mon, 5 Apr 2004, Charles Comstock wrote:

Vadim Nasardinov wrote:

And there is an insurmountable limitation (I think):

.

chars = words = lines = 0
str.each do |line|
chars += line.chomp.length
words += line.split
lines += 1
end

.

I don’t believe [Python] can carry outside state into its
“substitute for blocks”.

Feature-by-feature comparisons are useful insofar as they help you
understand differences and similarities between the two languages
being compared. Consider English and German, for example. The former
has an “insurmountable” limitation of not having explicit case
markings for the nominative, genitive, accusative, and dative cases
the way German does.

Now, this may be a useful thing to know, but it’s useless as far as
comparing the overall expressiveness, naturalness, and ease of use of
these two languages.

As Jim Weirich already pointed out in ruby-talk 96368, closures are a
poor man’s objects. One way of making a function carry state in
Python is to implement it as a callable object:

$ cat poor_mans_closure.py 
#!/usr/bin/python

class Foo:
    "A poor man's closure"

    def __init__(self, state):
        self.__call__ = self._call
        self.state = state

    def _call(self, x, y):
        return x * self.state + y


foo1 = Foo(1)
foo2 = Foo(42)

print foo1(2,3)
print foo2(2,3)

$ ./poor_mans_closure.py 
5
87

Yea but that doesn’t give you the guarenteed cleanup that the yield
syntax allows. In c++ this would be accomplished by using a guarded
object that would destruct when the scope was exited, there is no
comparable operation in java, without explictely declaring a wrapping
try / catch / finally. In C# V2.0 it is possible because of the
existance of yield. It is not possible in C# 1.0. It is possible in
ruby ala:

def setup_and_teardown
#code which must execute before code block a and b
yield
#code which must execute after code block a and b
end

setup_and_teardown() do
# operation a) which may fail but we want setup and cleanup code
# guarenteed
end

setup_and_teardown() do
# operation b) which may fail but we want setup and cleanup code
# guarenteed
end

Is there any sort of similar guarded block equivalent in python while
still allowing multiple blocks to be called and not requiring a complex
dispatcher routine?

Charles Comstock

···

On Monday 05 April 2004 11:08, Gavin Sinclair wrote:

Vadim Nasardinov wrote:

class Foo:
    "A poor man's closure"

    def __init__(self, state):
        self.__call__ = self._call
        self.state = state

    def _call(self, x, y):
        return x * self.state + y


foo1 = Foo(1)
foo2 = Foo(42)

print foo1(2,3)
print foo2(2,3)

Ah yes, in one fell swoop, demonstrating almost all the things I find
annoying about Python:

  • ‘self’ as the first parameter of methods, which is one of the sources
    of that whole “Python’s OO is a hack” thing. Now you can argue that
    it’s just a (very odd) style choice, but I can’t help but see that as
    ugly and hackish.
  • Defining a method with ‘n’ parameters, calling it with ‘n-1’
    parameters. An artifact of how Python’s OO is done, but so very
    confusing and ‘hackish’
  • So Many Underscores!! (self.call = self._call)???
  • Constructors look exactly like functions. No ‘new’ or any other
    similar hint that it’s a constructor.

All it’s really missing is the mishmash of functional, imperative and OO
programming like:

‘:’.join(map(lambda str: “0x%s”%str, mac_address.split(‘:’)))

Python and Ruby are effectively equals. They take about the same amount
of time to run a script, they have about the same level of abstraction.
They’re both dynamic typed. The only thing is: Python is ugly, Ruby
is pretty. Now sure, beauty is in the eye of the beholder, but my eye
prefers to behold Ruby. :slight_smile:

Ben

Would this work for you?

class Example
attr_reader :number
def initialize
@number = '1.0’
end
end

e = Example.new
puts e.number #=> will print 1.0

[rolo] My question is more related to why the concept is denied.

Hal Fulton wrote:

In practice, I’ve never used attr_writer. It’s hard to imagine
something useful being write-only and not readable. (Insert
Perl wisecrack here.)

I use it when I want to define a toggle, like the following:

class Foo
attr_writer :bar
def bar?
@bar
end
end

Not that I can’t use attr_accessor, I just don’t want to in this case.

···


That’s some catch, that Catch-22.
Oh, it’s the best there is.

Well, I’ll leave it up to you to decide whether or not it’s useful, but
RMagick defines special classes whose purpose is to collect optional
arguments when a method has a lot of optional arguments. For example,
the montage method has 15 optional arguments, so it creates a “montage
arguments” object and yields in that object’s context:

img.montage {
self.background_color = ‘red’
self.font = ‘Arial’

… etc.

}

These attributes are write-only.

I’m not claiming this was my idea, but offhand I forget where I saw it :slight_smile:

···

On Tue, 06 Apr 2004 06:26:28 +0900, Hal Fulton wrote:

In practice, I’ve never used attr_writer. It’s hard to imagine something
useful being write-only and not readable. (Insert Perl wisecrack here.)

And there is an insurmountable limitation (I think):

chars = words = lines = 0
str.each do |line|
chars += line.chomp.length
words += line.split
lines += 1
end

I don’t believe [Python] can carry outside state into its
“substitute for blocks”.

Feature-by-feature comparisons are useful insofar as they help you
understand differences and similarities between the two languages
being compared. Consider English and German, for example. The former
has an “insurmountable” limitation of not having explicit case
markings for the nominative, genitive, accusative, and dative cases
the way German does.

Doesn’t seem insurmountable to me! :slight_smile:

Now, this may be a useful thing to know, but it’s useless as far as
comparing the overall expressiveness, naturalness, and ease of use of
these two languages.

Very true. But a suggestion was made that Python has equivalent
functionality to Ruby’s blocks. Since blocks are central to the whole
Ruby experience, it’s worth testing that assertion. That’s the
context of my “insurmountable” claim above. I explicitly disclaimed
any opinion about whether Python could carry out the task in general.

As Jim Weirich already pointed out in ruby-talk 96368, closures are a
poor man’s objects. One way of making a function carry state in
Python is to implement it as a callable object:

$ cat poor_mans_closure.py 
#!/usr/bin/python
class Foo:
    "A poor man's closure"
    def __init__(self, state):
        self.__call__ = self._call
        self.state = state
    def _call(self, x, y):
        return x * self.state + y

Yep, and that operates at a lower level that Ruby’s features, which is
all I was trying to point out (and trying to do so objectively).

Cheers,
Gavin

···

On Tuesday, April 6, 2004, 3:07:54 AM, Vadim wrote:

On Monday 05 April 2004 11:08, Gavin Sinclair wrote:

[warning, long post :]

> That's a poor man's substitute.  Seriously.  It means you have to name
> everything you're doing, and pollute the function namespace *for no
> good reason*.  Yes, you can "do it", but it's ugly in comparison.

You don’t really pollute it much, because you can always use the same
name. The old name (and as such, the old definition too) is clobbered
by the new name. If you always use big L for this purpose,

Also, you can do the deffing inside functions too. So it’s not even
visible at module level.

True. That’s a pretty good compromise. I’d like to see Ruby support
“functions within functions” more properly, but there’s probably some
reason it doesn’t fit. At the moment, if you def within a def, your
inner def actually takes place in the outer scope. There’s a
workaround if you need it, but I dislike workarounds.

> Can you provide an implementation in the same style in Python?  It's
> not that I don't believe Python can count characters, lines and words
> in a string.  But I don't believe it can carry outside state into its
> "substitute for blocks".

Yes. Python supports lexical closures. The only gotcha is that you
can’t ‘assign’ the names of free variables to point to new objects,
i.e. it has to be the same object, though it can be mutated. So you
can use e.g. length 1 array to contain the other object.

That’s an unfortunate limitation. Just like Java. Given that
limitation, does it really count as a lexical closure? (I’m out of my
expert range there.)

The following code:


def get_char_counter():
chars = [0]

def filepusher(f):
    chars[0] += len(open(f).read())
    return chars[0]

return filepusher

acc = get_char_counter()

print acc(“/bootsect.dos”)
print acc(“/bootsect.dos”)


Prints out:


201
402

I’ve seen an example like that before. I don’t understand why the
return value changes. You are asking for a character count of an
unchanging file.

> # Connection: the database connection is automatically released at
> # the end of the block, including if there is an exception therein.

try - finally blocks are used for this in Python.

> # Transaction support: any exception in this block will cause a
> # "ROLLBACK".  If all is fine, a "COMMIT" is performed at the end.

try:
stuff()
except:
rollback()
else:
commit()

But I admit that the ruby approach seems cleaner here. It’s probably
easier to use and the logic doesn’t clutter the code.

> I'd like to see the same program written in Python.  I don't know the
> Python database interface, but I expect it would need to have explicit
> exception handling.  There would also be no automatic releasing of
> resources.

Back when I did db programming, I didn’t think it was such a big
deal. It seems bigger on the paper, though.

Quite right. But power steering in cars is not such a big deal,
either. It just becomes a hassle to live without once you’re used to
it.

> To prove me wrong, please present your best effort at translating the
> database programming example above into Python.  Of course, you may
> disagree with my frame of reference instead, but I think that's pretty
> good.

Yes, I do indeed disagree with your frame of reference. I just don’t
think this issue such big a deal.

It may not be a “big deal”, but that doesn’t invalidate the frame of
reference. It demonstrates higher-level programming as applied to a
very practical domain, which was the purpose of the exercise.

To do 10 transactions, you have to write 10 “try/finally” blocks. I, on
the other hand, only need to write 1 “try/finally” block.

To do 100 transactions, you have to write 100 “try/finally” blocks. I
only have to write 1.

And in fact, since the database library implements it for me, I don’t
have to write any. There will never be a superficial transaction-
related bug in my code, because there was no opportunity for mistyping
or omitting.

A higher degree of reuse == higher level programming.

And when you apply that across several domains instead of one, it
makes a big difference to your code as a whole.

you might want to go googling for
more elaborate discussion - code blocks have indeed been suggested for
Python, they just didn’t happen. It seems to pop up every now and
then, so perhaps they will be in Python some day if they are deemed
valuable enough.

I imagine it’s hard to see the value of code blocks as an “added
feature”. We’re only human, after all. They are only useful if the
pervade the language. That way, when you’re new to the language, you
see them being used in very useful ways. That familiarises you with
the concept, and then you go: “hang on, does that mean I can do ____
myself? Wow, what a cool feature!” That’s my experience, anyway.

That’s what really rocks about Ruby: people come up with excellent new
ways of applying the technology that I would (probably) never have
thought of. And they usually involve getting the computer to make the
programmer’s life much easier.

With other languages, the limits of discourse seem to be design
patterns (i.e. arrangements of classes) and refactoring techniques.
Those are certainly worthwhile, but Ruby’s flexibility seems to spread
the discussion into a new dimension. And some design patterns (e.g.
delegate, singleton) are directly implementable in Ruby. E.g.:

class DataManager
include Singleton
# …
end

DataManager.instance.do_something # the one and only instance

That’s another instance of writing the code (ensuring there’s only one
instance of a class) once and using it conveniently. I doubt there
are any other popular/practical languages that enable this level of
reuse.

I acknowledge that sometimes the techniques I speak of are more
focussed on “doing cool things” than “getting things done”. But there
is usually some overlap. I’d say Ruby is an engine-room of innovation
just as much as it is a practical programming language. Matz even said
(about continuations) “I just implement the feature; you have to work
out what to do with it.” (paraphrased)

My point is that many people benefit from this engine-room side of
Ruby. Just as nearly every feature of Ruby is swiped from or inspired
by another language, so Ruby is contributing to the evolution of
existing languages (Python, Perl) and design of “new” ones (Groovy).
The contribution comes about by demonstrating the worth of unorthodox
features and approaches.

It’s pretty common knowledge in our communities that Python used to
lack certain useful features and this situation was improved after
2.0 or 2.1 or thereabouts (e.g. inheriting built-in classes,
OO-vs-function-call interfaces). I’m sure it would be acknowledged
that Ruby was a factor in some of the changes. Well, I’m also pretty
confident that that process is not complete. Python needed to correct
some of its design mistakes; Ruby does too. Work is underway on Ruby
2.0, which will be a very bold break from the past.

That is some context in which to view the significance of things like
code blocks (and other features, to be sure). In fact, the point I
make above probably go a long way to explaining the “difference”
between Python and Ruby (the subject of this thread, after all). But
they are “just” my opinion, so it doesn’t necessarily make good fodder
for when the question invariably comes up.

Cheers,
Gavin

···

On Tuesday, April 6, 2004, 4:34:20 PM, Ville wrote:

Hehe…I’ll avoid the Perl wisecrack, but I can think of a couple scenarios
where attr_writer might be useful:

  1. True write-only data, such as a password. For example, you might have
    password=(pwd) to set the password, and verify_password(pwd) to compare the
    password to a given string, but no password reader method. This is
    especially useful if you store passwords in hashed form so that they cannot
    be retrieved once hashed.

  2. You might want to use a standard attr_writer generated writer method, but
    provide a custom reader method. I do this all of the time with boolean
    attributes, so I can add the ‘?’ to the end of the reader method.


Jason Voegele
Sodd’s Second Law:
Sooner or later, the worst possible set of circumstances is
bound to occur.

···

On Monday 05 April 2004 5:26 pm, Hal Fulton wrote:

 attr_reader :number     # Let outside world read this value
 attr_writer :number     #  "     "       "  set it
 attr_accessor :number   #  "     "       "  read or set it

In practice, I’ve never used attr_writer. It’s hard to imagine
something useful being write-only and not readable. (Insert
Perl wisecrack here.)

Ville Vainio wrote:

> # Connection: the database connection is automatically released at
> # the end of the block, including if there is an exception therein.

try - finally blocks are used for this in Python.

> # Transaction support: any exception in this block will cause a
> # "ROLLBACK".  If all is fine, a "COMMIT" is performed at the end.

try:
stuff()
except:
rollback()
else:
commit()

But I admit that the ruby approach seems cleaner here. It’s probably
easier to use and the logic doesn’t clutter the code.

Not only that, but in Ruby you don’t need to know about “rollback” or
“commit”. You also don’t need to know what kinds of exceptions might be
thrown.

That, to me, is the enormous difference between using blocks and using
exceptions.

The person writing the transaction-type method knows best what might
happen during the attempt. Let’s take the database example:

The database expert writes:

def do_transaction(arg)
try_number = 1
begin
db = dbh.open(arg)
yield(db)
rescue ConnectionError => e
# Ah, this type of error often happens when
# the server is under heavy load, we should try again
if try_number < MAX_TRIES
tries += 1
retry
end
# Too many retries
$stderr.puts “Too many retries!”
rescue ExplosionError
# Ah, this one is serious, we should probably
# make a big deal out of it
raise e
rescue NoSuchDatabaseError
# Oops, no such database, tell the user
$stderr.puts “No such database!”
ensure
db.close
end
end

The database user writes:

do_transaction(arg) {

db>
db.doStuff()
}

Here the person using the database doesn’t need to know which exceptions
are retryable, which are serious, etc. All they need to do is call it
with a block. The person writing the database stuff will make sure that
the appropriate thing happens at the appropriate time.

Now, there are lots of ways of doing this in other languages, but Ruby
is the first language I’ve seen that does it so easily, so commonly, and
so often. I’m always amazed when I start tracing what really happens
with certain block-using methods. What appears to be a simple call, and
is so easy to use, actually does all kinds of things behind the scenes,
often passing my block all around to different methods before actually
yielding to it.

I think the important thing isn’t that this is possible in Ruby, but
that it is a fundamental way in which the language is used. It’s like
recursion in functional languages. Many of them can do loops with
iteration or recursion, and most imperative languages can do both as
well. The real difference is that you’ll see recursion all over the
place in the functional languages, and it makes doing certain types of
things much easier and more obvious.

Ben