Forum: Ruby Cool: nil is a class that can have methods

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.
2c562374d9df94b5528a33eca627778f?d=identicon&s=25 Rob Burrowes (Guest)
on 2006-05-20 01:55
(Received via mailing list)
Excuse a newbee's enthusiasm, but this is really cool. It makes some
code that would have had to treat nil specially.


class NilClass
	def each
		yield(self)
	end
	def length
		0
	end
end

a = nil
a.each {|a| puts a.class }

Doing it at the Object level is even more handy for the what I was
playing with.

class Object
	def each
		yield(self)
	end
	def length
		if self == nil then 0 else 1 end
	end
end

a = nil
a.each {|a| puts a.length }

So every Object has an each method, which yields itself and has a
length of 1, so everything can be treated like an array.
Really neat language.
1fba4539b6cafe2e60a2916fa184fc2f?d=identicon&s=25 unknown (Guest)
on 2006-05-20 02:23
(Received via mailing list)
Hi --

On Sat, 20 May 2006, Rob Burrowes wrote:

> 	end
> 		yield(self)
> 1, so everything can be treated like an array.
> Really neat language.

Do be careful, though.  Remember that the changes you make to Object
and NilClass will be in effect for all the library code that you
include in your application, including the standard library, so if
anyone else is relying on the fact that some objects respond to "each"
and others don't, or that nil is dimensionless, something could go
wrong.


David
567898c496278341be69087507d5ed24?d=identicon&s=25 Jeff Rose (Guest)
on 2006-05-20 02:44
(Received via mailing list)
dblack@wobblini.net wrote:
>>         yield(self)
>> playing with.
>> a = nil
> and others don't, or that nil is dimensionless, something could go
> wrong.
>
>
> David
>

This is what namespace selectors would help fix right?  Was there ever a
decision made about how that would work?  Is it in 1.9 now?

-Jeff
2c562374d9df94b5528a33eca627778f?d=identicon&s=25 Rob Burrowes (Guest)
on 2006-05-20 02:54
(Received via mailing list)
Good point.

Looked sort of handy in the log filter I produced to generate gnuplot
files. Nil data has meaning in the plot, as distinct from a 0. It is
also handy to treat a single result in the same way as an array
result. The collation of the data, from the logs to the plot data
file,  is being driven by an XML description, with embedded ruby
expressions as XML attributes and text. These XML expressions get
converted into methods to parse the logs.  Having the targets of the
expressions work seamlessly, despite them being nil, single values or
arrays, was quite neat.

In the nil case, I wanted it to iterate and be printed as in the same
way as a non-nil value. In the plot data, a nil prints as '-'.  I
hadn't thought of other code expecting to fail when the value was nil
or not an array.  They probably wont want nil to have a to_s()
returning  '-' either, not that anything has failed doing this, in
this instance.

I am modifying it to output multiple rows, for generating Histograms,
so I will look at doing this in a less dangerous way.
0ca6e5c33d7e7ff901d75ff0b13d9e1c?d=identicon&s=25 Sam Roberts (Guest)
on 2006-05-20 04:21
(Received via mailing list)
Quoting rob@cs.auckland.ac.nz, on Sat, May 20, 2006 at 09:50:49AM +0900:
> In the nil case, I wanted it to iterate and be printed as in the same
> way as a non-nil value. In the plot data, a nil prints as '-'.  I
> hadn't thought of other code expecting to fail when the value was nil
> or not an array.  They probably wont want nil to have a to_s()
> returning  '-' either, not that anything has failed doing this, in
> this instance.

The temptation to change the core classes to work a little more like you
want is almost overwhelming... at least I have found it so. I guess in
this case you could do:

def gnuplot_each(v, &proc)
  if v.respond_to? :each
    v.each &proc
  else
    yield v
  end
end

then instead of doing

  data.each {..}

you could do

  gnuplot_each(data) {...}


cheers,
Sam
0ca6e5c33d7e7ff901d75ff0b13d9e1c?d=identicon&s=25 Sam Roberts (Guest)
on 2006-05-20 04:49
(Received via mailing list)
Quoting dblack@wobblini.net, on Sat, May 20, 2006 at 09:20:20AM +0900:
> Do be careful, though.  Remember that the changes you make to Object
> and NilClass will be in effect for all the library code that you
> include in your application, including the standard library, so if
> anyone else is relying on the fact that some objects respond to "each"
> and others don't, or that nil is dimensionless, something could go
> wrong.

Whats the current wisdom on how to do this more nicely?

I've got a library that added #to_time to the Date builtin... and its
conflicting with another much more commonly used library, that has done
the same thing, but give time in local TZ, not UTC. Oops...


Easy way, is to move my code out into a utility method

module Util
  def to_time(v)
    case v
    when Time
      v
    when Date
      ... cvt v to a Time ..
    else
     ... ?
    end
  end
end

but switches on class type are usally a bad thing... in OO code
generally, and in ruby in particular we are supposed to duck-type, and
its a real posibility that also DateTime, and perhaps even other date or
time classes might wander into my code. I'd really like not have this
kind of switch statement.

What to do?

I could change my method name from Date#to_time, to
Date#vpim_to_time_utc, basically namespacing my extension, and adding
one to Time as well.

I was also considering having some kind of hash:

module Util
  @@cvt_to_time_utc = {
    Time => lambda{|t| t },
    Date => lambda(|d| Time.utc(d.year, d.mon, d.day)
    }
  def self.cvt_to_time_utc(v)
    @@cvt_to_time_utc[v.class].call(v)
  end
end

Then 3rd party classes could register some handlers to convert to Time
in UTC, and I wouldn't have to modify my case statements.

But... what if they have

  class MyTime < Time
  end

That won't work... so I could maybe be more clever, search their
ancestry looking for something that is in my hash.... This is starting
to be a bit of work, and unfotunately similar to what ruby does for
method dispatch...

Maybe just using name mangling, using my library prefix, is the way to
go!

Thoughts?


Cheers,
Sam
2c562374d9df94b5528a33eca627778f?d=identicon&s=25 Rob Burrowes (Guest)
on 2006-05-20 05:46
(Received via mailing list)
Probably total sacrilege, but I was using this to have "nil" as a
valid value in  arithmetic and to treat numbers as if they were
arrays (in a limited sense). The latter is easy to fix, by storing
all numbers as 1 element arrays.  This solves the iterator on "nil"
too. All nil values will be in an array, so I just need to iterate on
the array.

Using "nil" in arithmetic is to differentiate  the no data state
(nil), from having seen a data point, even a 0 value. Probably best
to subclass Fixnum and build my own objects for arithmetic. Altering
the numeric classes is definitely a tempting thing though.

I have XML definitions of the source log file and the resulting
summary I want to get.
e.g.
a
<src>
	...
         <column id="sourceadjacentaddress">
                 <type>string</type>
         </column>
         <column id="destadjacentaddress">
                 <type>string</type>
         </column>
	...
</src>
<rslt>
	...
         <column id="link1">
           <test exp="@src_sourceadjacentaddress == '00-D0-41-68-
CF-56' || @src_destadjacentaddress == '00-D0-41-68-CF-56'"> </test>
           <value exp= "+= @src_d_tooctets+src_d_fromoctets">  </value>
           <type>int</type>
           <scale exp="/@groupby_duration.megabyte"> </scale>
           <format>%d</format>
         </column>
	...
</rslt>

This gets turned into ruby code that parses the log. It uses modified
column name as variables. The test expression determines if the
output column is modified, and the value expression is used to set or
alter the value. If the values start as nil, then adding a value,
will initialise them. Meeting further values, will result in them
being added on.

If multiple results are required (e.g. for a histogram), then a value
expression like
	<value exp= "+= [@src_d_tooctets, @src_d_fromoctets]">  </value>
results in the to and from traffic being added to an array result
called @rslt_link1, rather than a simple numeric result.


I did this by I adding "each" and "each_with_index" to every Object,
thus allowing numbers and nil values to have an iterator.
I replaced the Fixnum + with one that would add arrays to numbers,
giving an array result or Fixnum result, depending on the value being
added.
Also the Arrays class was modified to add two arrays, by adding the
elements at the same array index to each other, not concatenating the
arrays.
I then committed the ultimate sin, and added a plus method to nil.

After all these hacks, I can
	add a number to an array, and the first element will get altered.
	add an array to a number, and get an Array with the first element
altered
	add a number or Array to nil, and get that number or the Array



i.e.

class Object
	def each
		if self != nil then yield(self) end
	end
	def each_with_index
		if self != nil then yield(self,0) end
	end
	def length
		if self == nil then 0 else 1 end
	end
end

class Array
	def +(value)
		value.each_with_index { |x,i| self[i] += x }
		self
	end
end

class Fixnum
	alias oldPlus +
	def +(value)
		if(value.length == 1)
			self.oldPlus(value)
		else
			value.+(self)
		end
	end
end

class NilClass
	def +(value)
		value
	end
end

a = [1,3]
a += [5,2]
a.each {|b| puts b }
puts

a = 1
a += 2
a.each {|b| puts b }
puts

a += [5,2]
a.each {|b| puts b }
puts

a = nil
a += 10
a.each {|b| puts b }
puts

a = nil
a += [3,8]
a.each {|b| puts b }
puts

a = ["hello","I"]
a += [" world"," repent"]
a.each {|b| print b, " " }
puts
Dd76a12d66f843de5c5f8782668e7127?d=identicon&s=25 Mauricio Fernandez (Guest)
on 2006-05-20 09:55
(Received via mailing list)
On Sat, May 20, 2006 at 09:42:11AM +0900, Jeff Rose wrote:
> This is what namespace selectors would help fix right?  Was there ever a
> decision made about how that would work?  Is it in 1.9 now?

It's not in 1.9 yet. AFAIK this is the last thing we heard about
selector
namespaces coming from matz (albeit indirectly):

  "He has fixed the SelectorNamespace specification, however, which is
only in
  his mind now."

See http://redhanded.hobix.com/cult/rubyspameeting2006.html

As for the specification, [ruby-dev:27417] might give some clues.
This topic is locked and can not be replied to.