Forum: Ruby (Static) Constructors/Destructors in Ruby

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
5b1b6c0fe28f1441f44b200434b052c8?d=identicon&s=25 PrimaryKey (Guest)
on 2006-03-31 20:25
Hello!

After reading the Pickaxe book I noticed it provides almost no
information about object lifecycle and no mention of destructors. As a
Java/C# guy my gut reaction was: Wouldnâ??t it be nice to have a
â??unintializeâ? method to be called when the object is being destroyed?

There seems to be some form of destructor (called finalizers) in the
ObjectSpace module, but it seems to be an afterthought rather that an
integral feature of the language.

Another thing I find very surprising for a language having such a rich
OO model is the lack of static constructors (available in C# and somehow
in Java) and static destructors (something I always wanted to have in
Java/C#).

Based on that I will appreciate your thoughts on why a language having
such advanced OO features is lacking in these areas and is there any way
to emulate these.

Thanks
B97225f66bb5caac601b12735d430a0d?d=identicon&s=25 Marcin MielżyÅ?ski (Guest)
on 2006-03-31 20:40
(Received via mailing list)
PrimaryKey wrote:
> Hello!
>
> After reading the Pickaxe book I noticed it provides almost no
> information about object lifecycle and no mention of destructors. As a
> Java/C# guy my gut reaction was: Wouldnâ??t it be nice to have a
> â??unintializeâ? method to be called when the object is being destroyed?
>

Java does not have destructors either. finalize method is not guaranteed
to be called


> Another thing I find very surprising for a language having such a rich
> OO model is the lack of static constructors (available in C# and somehow
> in Java) and static destructors (something I always wanted to have in
> Java/C#).

Ruby has them. Every class definition is an executable code (public,
private, protected are methods) so you can alternate method definitions
with arbitrary code (in fact, this is much more powerful mechanism than
static blocks in Java and static constructors in C#)

lopex
70c8da82d09d3866222976ab8978133c?d=identicon&s=25 Daniel Nugent (Guest)
on 2006-03-31 20:52
(Received via mailing list)
Does anyone know under what conditions the finalizer won't be called?

On 3/31/06, Marcin Miel¿yñski <lopexx@autograf.pl> wrote:
> to be called
> static blocks in Java and static constructors in C#)
>
> lopex
>
>


--
-Dan Nugent

Don't Feel Like Typing?  Send me a voicemail:
http://odeo.com/sendmeamessage/DanNugent
B33ea5c12d767bfd1253940a960274f5?d=identicon&s=25 rmagick@gmail.com (Guest)
on 2006-03-31 20:56
(Received via mailing list)
PrimaryKey wrote:
> Hello!
>
> After reading the Pickaxe book I noticed it provides almost no
> information about object lifecycle and no mention of destructors. As a
> Java/C# guy my gut reaction was: Wouldn't it be nice to have a
> "unintialize" method to be called when the object is being destroyed?
>
> There seems to be some form of destructor (called finalizers) in the
> ObjectSpace module, but it seems to be an afterthought rather that an
> integral feature of the language.

Not so much an afterthought as discouraged.

Here's a good discussion about Ruby's finalizers:
http://www.rubygarden.org/ruby?RubyFromCpp
10d4acbfdaccb4eee687a428ca00a5d8?d=identicon&s=25 Jim Weirich (weirich)
on 2006-03-31 21:21
PrimaryKey wrote:
> There seems to be some form of destructor (called finalizers) in the
> ObjectSpace module, but it seems to be an afterthought rather that an
> integral feature of the language.

Actually, it is a very well thought out feature of the language.  In
Java, the finalizer is run when the object is eligible for garbage
collection.  But within the finalizer, you can create a new reference to
the object and make it ineligible for collection.

In Ruby, the finalizer is not run until *after* the object is collected.
Since the object itself is no longer around, there is no possibility of
even accidently creating a new reference to the object.  It also means
that the finalizer can't be an instance method (because the instance is
gone when the finalizer is run).

> Another thing I find very surprising for a language having such a rich
> OO model is the lack of static constructors (available in C# and somehow
> in Java) and static destructors (something I always wanted to have in
> Java/C#).

How would you use static constructors/destructors?  Perhaps we can show
you Ruby equivalents.

--
-- Jim Weirich
5b1b6c0fe28f1441f44b200434b052c8?d=identicon&s=25 PrimaryKey (Guest)
on 2006-03-31 21:58
> How would you use static constructors/destructors?  Perhaps we can show
> you Ruby equivalents.
>
> --
> -- Jim Weirich

Please consider the following (pseudo) C# example:

	public class AS400Server
	{
		static AS400 server;

		static AS400Server()
		{
			server = new AS400Connection("SERVERNAME");
		}

		static ~AS400Server()   // This is not real C#
		{
			server.disconnect();
		}

		static int GetServer()
		{
			return server;
		}
	}

I know this probably can be emulated using a singleton, I still believe
having static destructors will be nice because:

1. I like the constructor/destructor symmetry in C++
2. It can be helpful for meta-programming purposes. My impression is
most languages try to implement the traditional OO tools on class
(static) level

Thanks
E34b5cae57e0dd170114dba444e37852?d=identicon&s=25 Logan Capaldo (Guest)
on 2006-03-31 23:10
(Received via mailing list)
On Mar 31, 2006, at 2:58 PM, PrimaryKey wrote:

> 	{
> 		}
>
> 1. I like the constructor/destructor symmetry in C++
> 2. It can be helpful for meta-programming purposes. My impression is
> most languages try to implement the traditional OO tools on class
> (static) level
>
> Thanks
>
> --
> Posted via http://www.ruby-forum.com/.
>

class AS400Server
      @server = AS400Connection.new
      def self.get_server
             @server
       end
  end

As for a static deconstructor, can't really help you, I don't think
classes are really ever GCed until the very end, anyway. however, you
could do this:

class AS400Server
      at_exit { @server.disconnect }
end
F3b7109c91841c7106784d229418f5dd?d=identicon&s=25 Justin Collins (Guest)
on 2006-03-31 23:34
(Received via mailing list)
I believe this is the case if the garbage collector never runs. That is,
if the program exits before it is necessary.
Also, there are situations in which objects are never released, so the
finalize method won't be called then, either. If I recall correctly.

-Justin
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-03-31 23:56
(Received via mailing list)
PrimaryKey wrote:
> 		static AS400 server;
>
> 		static int GetServer()
> 		{
> 			return server;
> 		}
> 	}

I would ask first: does having one class per connection ("SERVERAME")
scale well? what if you need two servers...? This question isn't really
off topic, because once you accept this possibility, it becomes very
natural in ruby to do this:

class AS400Server
  def initialize(serv_name)
    @serv_name = serv_name
  end

  def connect
    # do something to connect to the server
    yield self
  ensure
    # do something to disconnet
  end
end

Then in your main code you might have:

def run_my_app
  AS400Server.new("SERVERNAME").connect do |serv|
    # do something with serv
  end
end

or

THE_SERVER = AS400Server.new("SERVERNAME")
def run_my_app
  THE_SERVER.connect do
    # do something with THE_SERVER
  end
end

Unless you exit with Kernel#exit! or the process is killed in a way that
ruby cannot catch (e.g., SIGKILL), the ensure clause will close the
server connection first. (Anyway, in the case of SIGKILL, a static
destructor would not be called either, right? Or can C# do that?)

(You can even roll the #connect method into initialize, if you want.
Passing the server object from yeild isn't really necessary, but you may
need to reference it in the block.)
B97225f66bb5caac601b12735d430a0d?d=identicon&s=25 Marcin MielżyÅ?ski (Guest)
on 2006-04-01 03:14
(Received via mailing list)
Jim Weirich wrote:

>
> In Ruby, the finalizer is not run until *after* the object is collected.
> Since the object itself is no longer around, there is no possibility of
> even accidently creating a new reference to the object.  It also means
> that the finalizer can't be an instance method (because the instance is
> gone when the finalizer is run).


This makes perfect sense since Ruby uses mark&sweep. But in Python is it
possible to supply __del__ method which seems to be an instance one
(correct me if I'm wrong), but the moment of GC cannot be determined
anyways. And as rmagick mentioned, explicit destrutors are not needed
anyways.

lopex
10d4acbfdaccb4eee687a428ca00a5d8?d=identicon&s=25 Jim Weirich (weirich)
on 2006-04-01 05:26
PrimaryKey wrote:
> Please consider the following (pseudo) C# example:

I think Logan Capaldo's answer is hits pretty close to your intention
here.  However, I have to comment because I've been retrofitting unit
tests in to a legacy (Java) code base this past week and I've come to
*hate* static initializers.

The code base does something similar to your example and the code is
impossible to unit test without some restructuring.  Since the database
connection is made in static initializers, I can't even load the class
without having the infrastructure for a database connection available.
Yuck!

I've come to consider complex static initializers (e.g. making DB
connections in the initilizer) a thing of "evil".

Ok, rant over. ;)

> [...]
> 2. It can be helpful for meta-programming purposes. My impression is
> most languages try to implement the traditional OO tools on class
> (static) level

I'm not getting statement #2 above.

Thanks.

--
-- Jim Weirich
47b36de21d7ecbc824c81d24802a6290?d=identicon&s=25 Minkoo Seo (pool007)
on 2006-04-01 08:20
(Received via mailing list)
Hi.

PrimaryKey wrote:
> Based on that I will appreciate your thoughts on why a language having
> such advanced OO features is lacking in these areas and is there any way
> to emulate these.

Actually, destructor in Java, i.e., finalize( ), does not well behave.
Moreover, using it properly is next to impossible. Have a look at:
http://developers.sun.com/learning/javaoneonline/2...

Here's the recap:
(1) Java finalizer is not guranteed to run.
(2) Java finalizer is not executed in the main thread. Instead, it runs
in its own thread. Hence, synchronization mechanism for read/write
visibility and concurrency control is required.
(3) If java finalizer access a certain object which is already garbage
collected, then the object will resurrect. To avoid this situation, you
should not access garbage collected object while finalizing, but it is
not easy because there's no certain order in garbage collection.

Instead of relying on finalize, you'd better use dispose pattern like:

class Foo
   def dispose
      # release all non-managed resources
   end
end

You can find similar interface, IDisposable, in C#. The reason is that
people developed C# already found that writing destructor for releasing
unmanaged resources is not simple than writing explicit dispose method.

Sincerely,
Minkoo Seo
Ffcb418e17cac2873d611c2b8d8d891c?d=identicon&s=25 Benjohn Barnes (Guest)
on 2006-04-01 11:58
(Received via mailing list)
On 31 Mar 2006, at 20:58, PrimaryKey wrote:

>
>
> I know this probably can be emulated using a singleton,

The singleton design pattern certainly can do that, and is easily
used in Ruby by including module "Singleton" (except for the
destruction part - although I expect a finaliser could do that for
you, but I've not used them myself).

When you started to talk about these, I thought you were after
'structors for a class, rather than an instance. These are, of
course, very easy in Ruby, as a class is just an object like any
other. In fact, you can have little factories that will pump out
classes, if you want.


> I still believe
> having static destructors will be nice because:
>
> 1. I like the constructor/destructor symmetry in C++

Actually, I like the c++ destructors too. I'd say they are one of the
very few things that I miss about it :) I quite often made use of a
"stack object". It's an object with no interface except for it's
'structors. You would declare them "on the stack" in c++ (ie, without
using new), so they are local to a code block. They're really useful
for doing setup within a code block and ensuring that teardown
happens when you exit it. They seem like a DRY way to specify the use
of a mode within a code block:

class StackObject
{
public:
	StackObject() {do_setup_code();}
	~StackObject() {do_teardown_code();}

private:
	void do_setup_code();
	void do_teardown_code();
};

And to use...

{
	// Setup resource.
	StackObject stack_object;

	// Do stuff with the mode being setup by the stack object.

	// Tear down happens automatically at block exit.
}

However, a very similar idea is possible in Ruby, and can be achieved
with passing code blocks...

def run_within_mode
	setup_code
	yield
ensure
	teardown_code
end

And to use:


# Here, the mode is not in use yet.
run_within_mode do
	# Anything here is happening with the mode! Hooray!
end
# And the mode's gone again.

As well as ensuring the resource is cleaned up, both of these
approaches are exception safe too.

Cheers,
	Ben
2c7c807a1df0c76a8fc823c709b501a9?d=identicon&s=25 Victor Shepelev (Guest)
on 2006-04-01 12:11
(Received via mailing list)
> def run_within_mode
> run_within_mode do
> 	# Anything here is happening with the mode! Hooray!
> end
> # And the mode's gone again.
>
> As well as ensuring the resource is cleaned up, both of these
> approaches are exception safe too.

I like block idea, but must say that blocks can't give full replacement
for
C++'s RAII ideom:

C++:

{
  File f1,f2,f3;
  Socket s1,s2;
  Database d;
} //here all f1,f2,f3,s1,s2,d being closed

Ruby:

???

>
> Cheers,
> 	Ben

Victor.
Ffcb418e17cac2873d611c2b8d8d891c?d=identicon&s=25 Benjohn Barnes (Guest)
on 2006-04-01 12:26
(Received via mailing list)
On 1 Apr 2006, at 11:10, Victor Shepelev wrote:
> } //here all f1,f2,f3,s1,s2,d being closed
>
> Ruby:
>
> ???

:) It's such a fresh beautiful day outside... and here I am talking
tech, when spring is springing :)

Ok, in Ruby, each of those resources could easily be turned in to a
method that will run a block. What you want to be able to do then, is
easily compose those methods in to one, and have that run something
within the context of resources being available?

Something along the lines of (unchecked code warning!)...?

def run_with_resources(*resources, &block)
	if resources == []
		yield
	else
		send(resources.slice(0)) {run_with_resources(*resources, &block)}
	end
end

You can then do...

run_with_resources
(:file_handle, :db_connection, :socket_and_things, :other_groovy_bits) d
o
	#Wooo - look at all these resources!
end

It wouldn't be a big extension to collect up the resources passed in,
and pop them in a map, or something. I don't think it would be hard
to give the resources arguments either.

Cheers,
	Ben
Ffcb418e17cac2873d611c2b8d8d891c?d=identicon&s=25 Benjohn Barnes (Guest)
on 2006-04-01 12:35
(Received via mailing list)
On 1 Apr 2006, at 11:25, Benjohn Barnes wrote:
> :) It's such a fresh beautiful day outside... and here I am talking
> tech, when spring is springing :)

It really is!

> 	else
> 		send(resources.slice(0)) {run_with_resources(*resources, &block)}

That was supposed to be:
	send(resources.slice!(0)) {run_with_resources(*resources, &block)}

The ! after slice is vital!
2c7c807a1df0c76a8fc823c709b501a9?d=identicon&s=25 Victor Shepelev (Guest)
on 2006-04-01 12:38
(Received via mailing list)
> > C++'s RAII ideom:
> >
> > ???
>
> :) It's such a fresh beautiful day outside... and here I am talking
> tech, when spring is springing :)

Hmmm... Not so beautiful/springy day in Kharkov, Ukraine :)

> 	else
> 	#Wooo - look at all these resources!
> end
>
> It wouldn't be a big extension to collect up the resources passed in,
> and pop them in a map, or something. I don't think it would be hard
> to give the resources arguments either.

Yes, I know all those trick (I'm not a VERY newbie :) - my point was
"blocks
not always good alternative for RAII".

BTW, I recalled I saw some library at RAA, which does the stuff. But I
don't
remember it's name (because in real life it is not as necessary as in
dumb
examples ;)

> Cheers,
> 	Ben

Victor.
Ffcb418e17cac2873d611c2b8d8d891c?d=identicon&s=25 Benjohn Barnes (Guest)
on 2006-04-01 13:30
(Received via mailing list)
On 1 Apr 2006, at 11:36, Victor Shepelev wrote:
> Yes, I know all those trick (I'm not a VERY newbie :) - my point
> was "blocks
> not always good alternative for RAII".

:) I thought it was quite clever, I was feeling very pleased with
myself :)

> BTW, I recalled I saw some library at RAA, which does the stuff.
> But I don't
> remember it's name (because in real life it is not as necessary as
> in dumb
> examples ;)

Well, that's the thing though, really :)
47b1910084592eb77a032bc7d8d1a84e?d=identicon&s=25 Joel VanderWerf (Guest)
on 2006-04-01 22:30
(Received via mailing list)
Victor Shepelev wrote:
> Hmmm... Not so beautiful/springy day in Kharkov, Ukraine :)

Nor in Northern California. It's so wet the sidewalks are turning green.

>> Ok, in Ruby, each of those resources could easily be turned in to a
>> method that will run a block. What you want to be able to do then, is
>> easily compose those methods in to one, and have that run something
>> within the context of resources being available?

Here's an idea:

require 'socket'

class ResourceMgr
  def [](*resources)
    @resources = resources
  end

  def cleanup
    @resources.reverse_each do |resource, cleanup_method|
      resource.__send__ cleanup_method || :close
    end
  end
end

def with_resources
  res = ResourceMgr.new
  yield res
ensure
  res.cleanup
end

rl_test = nil

with_resources do |res|
  res[
    f = File.open("/tmp/file", "w"),
    s = UDPSocket.open,
    [t = Thread.new {sleep}, :kill]
  ]

  rl_test = [f,s,t]
  p rl_test
  # use resources here
end

p rl_test

__END__

Output:

[#<File:/tmp/file>, #<UDPSocket:0xb7dca598>, [#<Thread:0xb7dca4e4
sleep>, :kill]]
[#<File:/tmp/file (closed)>, #<UDPSocket:0xb7dca598>,
[#<Thread:0xb7dca4e4 dead>, :kill]]
5b1b6c0fe28f1441f44b200434b052c8?d=identicon&s=25 Primary Key (Guest)
on 2006-04-02 01:57
Jim Weirich wrote:

>> 2. It can be helpful for meta-programming purposes. My impression is
>> most languages try to implement the traditional OO tools on class
>> (static) level
>
> I'm not getting statement #2 above.

Please consider the evolution of the class-level language features:

1. C++ - static member variables
2. Java - static members and static initialization blocks
3. C# - static members and static constructors

I think there is a clear pattern of implementing more and more of the
object-level OO features on class (static) level. I believe the next
logical step will be a language support for static destructors. As far
as meta-progamming is concerned the static features will beef up the
class level in the object-class-metaclass triad. Again, this is just the
personal opinion of a "evil" legacy(Java) programmer :-)

Thanks again,
Primary Key
10d4acbfdaccb4eee687a428ca00a5d8?d=identicon&s=25 Jim Weirich (weirich)
on 2006-04-02 05:29
Primary Key wrote:
> Jim Weirich wrote:
>
>>> 2. It can be helpful for meta-programming purposes. My impression is
>>> most languages try to implement the traditional OO tools on class
>>> (static) level
>>
>> I'm not getting statement #2 above.
>
> Please consider the evolution of the class-level language features:
>
> 1. C++ - static member variables
> 2. Java - static members and static initialization blocks
> 3. C# - static members and static constructors
>
> I think there is a clear pattern of implementing more and more of the
> object-level OO features on class (static) level.

Ok, at least for C-style languages I won't argue the point (although I'm
not entirely clear what a static constructor is ... other than a factory
function).

> [...] As far
> as meta-progamming is concerned the static features will beef up the
> class level in the object-class-metaclass triad.

I'm still unclear on how static methods do anything to support
meta-progarmming.  Perhaps an example might prove illuminating.

--
-- Jim Weirich
02f36d0f9d518cc75ac6ee8a6644f982?d=identicon&s=25 David Holroyd (Guest)
on 2006-04-02 12:53
(Received via mailing list)
On Sat, Apr 01, 2006 at 03:25:23AM +0900, PrimaryKey wrote:
> [...] static destructors (something I always wanted to have in
> Java/C#).

A bit off-topic, but note,

  http://java.sun.com/docs/books/vmspec/html/Concept...


dave
Dd76a12d66f843de5c5f8782668e7127?d=identicon&s=25 Mauricio Fernandez (Guest)
on 2006-04-02 13:33
(Received via mailing list)
On Sat, Apr 01, 2006 at 06:31:42AM +0900, Justin Collins wrote:
> I believe this is the case if the garbage collector never runs. That is,
> if the program exits before it is necessary.
> Also, there are situations in which objects are never released, so the
> finalize method won't be called then, either. If I recall correctly.

The finalizers will be run on exit

    ObjectSpace.define_finalizer(a = "", lambda{ puts "EXECUTED" })
    END{ puts "leaving" }
    puts "hi"
    RUBY_VERSION                                       # => "1.8.4"
    # >> hi
    # >> leaving
    # >> EXECUTED

But you can bypass them with Kernel#exit!

    batsman@tux-chan:~/mess/current$ cat exit_bang.rb
    ObjectSpace.define_finalizer(a = "", lambda{ puts "EXECUTED" })
    END{ puts "leaving" }
    puts "hi"
    puts RUBY_VERSION
    exit!
    batsman@tux-chan:~/mess/current$ ruby exit_bang.rb
    hi
    1.8.4
F3b7109c91841c7106784d229418f5dd?d=identicon&s=25 Collins, Justin (Guest)
on 2006-04-02 22:05
(Received via mailing list)
To clarify, I was talking about Java.

-Justin
9871033d29efd4972049a5c232ced7f1?d=identicon&s=25 Primary Key (Guest)
on 2006-04-03 02:35
Jim Weirich wrote:
> I'm still unclear on how static methods do anything to support
> meta-progarmming.  Perhaps an example might prove illuminating.

I am not sure this will be compelling enough, but here we go:

require 'dbi'

class Connection
	@@driver = 'DBI:ADO:Provider=SQLOLEDB;Data Source=...;Initial
Catalog=...;User Id=...;Password=...;trusted_connection=yes'

	def initialize
		@dbh = DBI.connect(@@driver)
		@is_closed=false
	end

	def close
		@dbh.disconnect
		@is_closed=true
	end

	def closed?
		@is_closed
	end

	# MSSQL Server specific
	def columns(table_name)
		table_name = table_name.to_s if table_name.is_a?(Symbol)
		table_name = table_name.split('.')[-1] unless table_name.nil?
		sql = "SELECT COLUMN_NAME as ColName, COLUMN_DEFAULT as DefaultValue,
DATA_TYPE as ColType, " +
			 "COL_LENGTH('#{table_name}', COLUMN_NAME) as Length,
COLUMNPROPERTY(OBJECT_ID('#{table_name}'), " +
			 "COLUMN_NAME, 'IsIdentity') as IsIdentity, NUMERIC_SCALE as Scale "
+
			 "FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '#{table_name}'"
		columns = []
		result = @dbh.select_all(sql)
		result.each { |field| columns << field }
		columns
	end
end

class MyClass
	# Static "constructor"
	def self.create(table)
		@@table = table
		@@connection = Connection.new

		columns = @@connection.columns(table)
		columns.each { |column|
		      property = 'fld'+column[0]
		      self.class_eval(%Q[
			def #{property}
			  @#{property}
			end
			def #{property}=(value)
			  @#{property} = value
			end
		      ])
		}
	end

	# Static "destructor"
	def self.destroy()
		@@connection.close unless @@connection.closed?
	end
end

MyClass.create('USERS')

table1 = MyClass.new
table2 = MyClass.new

table1.methods.each {|m| puts m if m =~ /^fld/ }

MyClass.destroy
This topic is locked and can not be replied to.