--------------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:&nbsp; 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&nbsp;have appended some source code at&nbsp; 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>&nbsp;&nbsp;&nbsp; on 01/07/12, Keith Hodges &lt;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.&nbsp; 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.&nbsp; 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 &lt;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 &lt;HERE> in a quick and elegant manner (i.e.
like a throw catch
<br>|but without the semantics)
<p>Without using catch nor rescue?&nbsp; No (Sorry for negative answers).
<p>But this is intersting.&nbsp; If someone would come up with nice solution
<br>proposal, I'd like to consider implementing it (maybe).
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
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>&nbsp; attr_reader :name</font></tt>
<br><tt><font size=-2>&nbsp; attr_reader :theCaller</font></tt>
<br><tt><font size=-2>&nbsp; attr_reader :theBinding</font></tt>
<br><tt><font size=-2>&nbsp; def initialize(aString='')</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; @name = aString</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; @theCaller = caller</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; @theBinding = binding</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def debug</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; @debug=true if !isDefaultNull?</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; self</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def isDefaultNull?</font></tt>
<br><tt><font size=-2>&nbsp;self&nbsp; === NULL</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def null?</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; true</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def nil?</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; true</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def ==( a )</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; a.nil?</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def new( *args )</font></tt>
<br><tt><font size=-2>&nbsp;Null.new( *args )</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp;</font></tt>
<br><tt><font size=-2>&nbsp; def to_s</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; return 'NULL' if isDefaultNull?</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; 'null' + @name</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def method_missing( aSymbol, *args )</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; puts("XX-" + theCaller.join("\n")
+ aSymbol.to_s + "-XX" ) if (@debug == true)</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; self</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def respond_to?</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; true</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def hash</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; nil.hash</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp;</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>&nbsp; def null?</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; false</font></tt>
<br><tt><font size=-2>&nbsp; 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>&nbsp; return(NULL) if (args.size == 0)</font></tt>
<br><tt><font size=-2>&nbsp; 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 &lt; Lapidary::TestCase</font></tt>
<br><tt><font size=-2>&nbsp;</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>&nbsp; def testThisNull</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertBlock('null should be identical
to NULL') { null ===&nbsp; NULL }</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertEqual( null.name , '' )</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertEqual( null.to_s , 'null'
)</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def testNULLnew</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; myNull = NULL.new</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; puts myNull.theBinding.inspect</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertEqual( myNull.theCaller
, '' )</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def testNamedNull</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; myNull = null('Eric')</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertEqual( 'Eric', myNull.name
)</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertEqual( 'nullEric', myNull.to_s
)</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def testNullAbsorbsUndefinedMessages</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertEqual( null, null.help.me.to.run.this.program.jimmy
)</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def testNullGlobalIsSingleton</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; tmpNull=NULL</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; require 'Null'</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertBlock { tmpNull === NULL
}</font></tt>
<br><tt><font size=-2>&nbsp; end</font></tt>
<br><tt><font size=-2>&nbsp; def testNullDebug</font></tt>
<br><tt><font size=-2>&nbsp;&nbsp;&nbsp; assertEqual( null, null('Mine').debug.testing.one.two.three
)</font></tt>
<br><tt><font size=-2>&nbsp; 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--