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