From: Keith Hodges Date: 2001-10-26T21:22:40+09:00 Subject: [ruby-talk:23428] Re: Null Pattern --------------CC4D4D01074E3372F83EC9CB Content-Type: text/plain; charset=us-ascii Content-Transfer-Encoding: 7bit Have you had any further thoughs on this matz? as far as a "nice solution proposal", the only thing that I can suggest is that the special global object "null" is defined. null would be an instance of class Null. this allows a developer to use the global "null" or a user instanciated named "null" which may be is useful for debugging. 1. Instances of class Null, are handled specially by: if (condition) then ... else ... end and do not execute either the then or the else clauses. 2. Instances of class Null either simply implemetn method_missing to return null, or have some way to skip the call cahin as indicated in Q3 below. I have appended some source code at the end of this email, which is far from final but works sufficiently to test the concepts out. cheers Keith -------------------------------- Yukihiro Matsumoto wrote: > Hi, > > In message "[ruby-talk:17721] Null Pattern" > on 01/07/12, Keith Hodges writes: > > |Q.1 What is the best way to define a new global constant like "nil" (i.e. lowercase). > > There's only one way: hack the parser. Pseudo variables: nil, false, > true, self, __FILE__, and __LINE__ are built in Ruby syntax. > > |Q.2 I would like to be able to obtain information about the instanciator of aNull > |so far I have found Kernal#binding and Kernal#caller but I cant find out how to use > |these to get > |the information that I want, which is "Object, and method that instanciated me." > > Currently, no. new caller interface is in RCR. > > |Q.3 given the code below is there a way to exit a call chain as follows > | > |a=null > |a.nice.way.to.avoid.testing.for.nil.all.of.the.time > |#I want to get as fast as possible! > | > |at present my implementation of Null#method_missing returns self. This means that the > |code above calls Null#method_missing on each "undefined message eating null" method > |call, 11 times! > | > |what I was wondering is whether there is a way to jump from a.nice (implemented by > |Null#method_missing) to in a quick and elegant manner (i.e. like a throw catch > |but without the semantics) > > Without using catch nor rescue? No (Sorry for negative answers). > > But this is intersting. If someone would come up with nice solution > proposal, I'd like to consider implementing it (maybe). > > matz. > ------------------------ #!/usr/bin/env ruby #This is a first attempt at supporting the Null pattern in Ruby #I couldnt find a detailed explanation of the Null pattern on the web #so this is a cleanroom implementation and there are probably things #I have forgotton class Null attr_reader :name attr_reader :theCaller attr_reader :theBinding def initialize(aString='') @name = aString @theCaller = caller @theBinding = binding end def debug @debug=true if !isDefaultNull? self end def isDefaultNull? self === NULL end def null? true end def nil? true end def ==( a ) a.nil? end def new( *args ) Null.new( *args ) end def to_s return 'NULL' if isDefaultNull? 'null' + @name end def method_missing( aSymbol, *args ) puts("XX-" + theCaller.join("\n") + aSymbol.to_s + "-XX" ) if (@debug == true) self end def respond_to? true end def hash nil.hash end end class Object def null? false end end #allows users to refer to "null" #or a named null('Nemo') NULL=Null.new def null( *args ) return(NULL) if (args.size == 0) return(Null.new(*args)) end #------------------------------------------------------ =begin =TC_Null.rb Contains tests for the Null class =end require 'Lapidary/TestCase' require 'Null' #due to the wierd behaviour of Null it could be hard to test! class TC_Null < Lapidary::TestCase #Properties of Null: #there is a singleton use case #can have individual instances if desired #ignores unknown messages (instead of raising erros like nil). #collect data of the original receiver for debugging #easy for user to refer to similarly to 'nil' as 'null' #users reference def testThisNull assertBlock('null should be identical to NULL') { null === NULL } assertEqual( null.name , '' ) assertEqual( null.to_s , 'null' ) end def testNULLnew myNull = NULL.new puts myNull.theBinding.inspect assertEqual( myNull.theCaller , '' ) end def testNamedNull myNull = null('Eric') assertEqual( 'Eric', myNull.name ) assertEqual( 'nullEric', myNull.to_s ) end def testNullAbsorbsUndefinedMessages assertEqual( null, null.help.me.to.run.this.program.jimmy ) end def testNullGlobalIsSingleton tmpNull=NULL require 'Null' assertBlock { tmpNull === NULL } end def testNullDebug assertEqual( null, null('Mine').debug.testing.one.two.three ) end end require 'Lapidary/UI/Console/TestRunner' Lapidary::UI::Console::TestRunner.run(TC_Null) --------------CC4D4D01074E3372F83EC9CB Content-Type: text/html; charset=us-ascii Content-Transfer-Encoding: 7bit Have you had any further thoughs on this matz?

as far as a "nice solution proposal", the only thing that I can suggest is that the special global object "null" is defined.
null would be an instance of class Null.

this allows a developer to use the global "null" or a user instanciated named "null" which may be is useful for debugging.

1. Instances of class Null, are handled specially by:  if (condition) then ... else ... end and do not execute either the then or the else clauses.
2. Instances of class Null either simply implemetn method_missing to return null, or have some way to skip the call cahin as indicated in Q3 below.

I have appended some source code at  the end of this email, which is far from final but works sufficiently to test the concepts out.

cheers

Keith
--------------------------------
Yukihiro Matsumoto wrote:

Hi,

In message "[ruby-talk:17721] Null Pattern"
    on 01/07/12, Keith Hodges <K.Hodges@ftel.co.uk> writes:

|Q.1 What is the best way to define a new global constant like "nil" (i.e. lowercase).

There's only one way: hack the parser.  Pseudo variables: nil, false,
true, self, __FILE__, and __LINE__ are built in Ruby syntax.

|Q.2 I would like to be able to obtain information about the instanciator of aNull
|so far I have found Kernal#binding and Kernal#caller but I cant find out how to use
|these to get
|the information that I want, which is "Object, and method that instanciated me."

Currently, no.  new caller interface is in RCR.

|Q.3 given the code below is there a way to exit a call chain as follows
|
|a=null
|a.nice.way.to.avoid.testing.for.nil.all.of.the.time
|#I want to get <HERE> as fast as possible!
|
|at present my implementation of Null#method_missing returns self. This means that the
|code above calls Null#method_missing on each "undefined message eating null" method
|call, 11 times!
|
|what I was wondering is whether there is a way to jump from a.nice (implemented by
|Null#method_missing) to <HERE> in a quick and elegant manner (i.e. like a throw catch
|but without the semantics)

Without using catch nor rescue?  No (Sorry for negative answers).

But this is intersting.  If someone would come up with nice solution
proposal, I'd like to consider implementing it (maybe).

                                                        matz.

------------------------
#!/usr/bin/env ruby

#This is a first attempt at supporting the Null pattern in Ruby
#I couldnt find a detailed explanation of the Null pattern on the web
#so this is a cleanroom implementation and there are probably things
#I have forgotton

class Null
  attr_reader :name
  attr_reader :theCaller
  attr_reader :theBinding
  def initialize(aString='')
    @name = aString
    @theCaller = caller
    @theBinding = binding
  end
  def debug
    @debug=true if !isDefaultNull?
    self
  end
  def isDefaultNull?
 self  === NULL
  end
  def null?
    true
  end
  def nil?
    true
  end
  def ==( a )
    a.nil?
  end
  def new( *args )
 Null.new( *args )
  end
 
  def to_s
    return 'NULL' if isDefaultNull?
    'null' + @name
  end
  def method_missing( aSymbol, *args )
    puts("XX-" + theCaller.join("\n") + aSymbol.to_s + "-XX" ) if (@debug == true)
    self
  end
  def respond_to?
    true
  end
  def hash
    nil.hash
  end
 
end

class Object
  def null?
    false
  end
end

#allows users to refer to "null"
#or a named null('Nemo')

NULL=Null.new

def null( *args )
  return(NULL) if (args.size == 0)
  return(Null.new(*args))
end

#------------------------------------------------------
=begin
=TC_Null.rb
Contains tests for the Null class
=end

require 'Lapidary/TestCase'
require 'Null'

#due to the wierd behaviour of Null it could be hard to test!

class TC_Null < Lapidary::TestCase
 
#Properties of Null:
#there is a singleton use case
#can have individual instances if desired
#ignores unknown messages (instead of raising erros like nil).
#collect data of the original receiver for debugging
#easy for user to refer to similarly to 'nil' as 'null'

#users reference
  def testThisNull
    assertBlock('null should be identical to NULL') { null ===  NULL }
    assertEqual( null.name , '' )
    assertEqual( null.to_s , 'null' )
  end
  def testNULLnew
    myNull = NULL.new
    puts myNull.theBinding.inspect
    assertEqual( myNull.theCaller , '' )
  end
  def testNamedNull
    myNull = null('Eric')
    assertEqual( 'Eric', myNull.name )
    assertEqual( 'nullEric', myNull.to_s )
  end
  def testNullAbsorbsUndefinedMessages
    assertEqual( null, null.help.me.to.run.this.program.jimmy )
  end
  def testNullGlobalIsSingleton
    tmpNull=NULL
    require 'Null'
    assertBlock { tmpNull === NULL }
  end
  def testNullDebug
    assertEqual( null, null('Mine').debug.testing.one.two.three )
  end

end

require 'Lapidary/UI/Console/TestRunner'
Lapidary::UI::Console::TestRunner.run(TC_Null) --------------CC4D4D01074E3372F83EC9CB--