[Ironruby-core] Experimentation

Tomas Matousek Tomas.Matousek at microsoft.com
Sun Oct 14 01:54:19 EDT 2007


1)      Change the signatures to:
        [RubyMethodAttribute("scan", RubyMethodAttributes.PublicInstance)]
        public static List<object>/*!*/ Scan(MutableString/*!*/ self, [NotNull]MutableString/*!*/ searchStr)

        [RubyMethodAttribute("scan", RubyMethodAttributes.PublicInstance)]
        public static object Scan(CodeContext/*!*/ context, MutableString/*!*/ self, [NotNull]BlockParam/*!*/ block, [NotNull]MutableString/*!*/ searchStr)


a)      Block is a special parameter and it must follow self parameter. The order of parameters is:

*         context

*         self

*         block

*         mandatory parameters

*         optional parameters

*         params array (rest parameters)

b)      Specify [NotNull] for parameters that cannot be null in order to select the overload. You can assume that parameters doesn't have null value when called from a DLR language. It's not a CLR attribute though so it doesn't prevent a non-dynamic languages to pass null. Since the library methods are not supposed to be called directly (only Ruby runtime should invoke the method), you don't need to check for non-nullity at runtime. Context parameter is always non-null (no need to check for null at run-time). Self parameter is also non-null unless the method is a module method or an instance method of NilClass.

c)       Annotate types by /*!*/ annotation if you assume them to be non-null. Although the annotation doesn't affect run-time behavior at all (being a comment, it's ignored by C#) it is useful for static analysis and expresses your assumptions.

d)      Note also that unless marked by NotNull attribute, BlockParam is nullable. Hence the overload might be eligible for invocation even though no block has been passed. A null reference is used if the block is not specified in a call site and there is no overload that matches better. So a single overload with BlockParam parameter also works. It depends on the semantics of the method which variant to chose. If presence of the block significantly changes the behavior of the method then it's probably better to have two overloads. Code like [RubyMethod("foo")]public static Foo(... block ...) { if (block != null) { 1st overload implementation } else { 2nd overload implementation} } should be avoided if possible; two overloads should be defined instead. On the other hand, if the implementation almost doesn't depend on whether the block is present or not (it only affects a small part of the implementation) then it's probably better to have a single overload.



2)      Check out dynamic site in Thread.CreateThread. It should do what you need. The magic is in ArgumentKind.List (splat).

3)      Feel free to patch the Rakefile. Note however, that we are going to change the shape of libraries a little bit (in particular move Builtins to IronRuby.Libraries.dll), so it might be necessary to adjust the script again afterwards.

Note that MutableString has an instance method IndexOf, so you don't need to convert to a CLR string (the method internally makes the conversion but that's only provisional implementation).

Tomas

From: ironruby-core-bounces at rubyforge.org [mailto:ironruby-core-bounces at rubyforge.org] On Behalf Of Curt Hagenlocher
Sent: Saturday, October 13, 2007 8:16 PM
To: ironruby-core at rubyforge.org
Subject: [Ironruby-core] Experimentation


I thought I'd implement some missing members on the String class in order to get my feet wet and start to understand the software.  I chose String.scan on the grounds that it was a fairly common function (between 20 and 30 references in the standard library) with straightforward semantics, but one which requires dealing with overloads and blocks.

There are basically four variations of this function:
String.scan <string>
String.scan <regexp>
String.scan <string>, <block>
String.scan <regexp>, <block>

I've attempted to implement each of these, and believe that all but the last are correct.  The <string> variations are implemented in two different flavors for both CLR strings and mutable strings.  A patch can be found at http://hagenlocher.org/software/MutableString.scan.patch.txt

The two issues I ran into are as follows:
1) The overload mechanism is picking the wrong method at runtime.  Here are two of the function prototypes:

[RubyMethodAttribute("scan", RubyMethodAttributes.PublicInstance)]
public static List<object> Scan(MutableString/*!*/ self, MutableString/*!*/ searchStr)
[RubyMethodAttribute("scan", RubyMethodAttributes.PublicInstance)]
public static object Scan(CodeContext/*!*/ context, MutableString/*!*/ self, MutableString searchStr, BlockParam block)




When I run from rbx.exe, I get the following:
>>> "hello world".scan("l")
=> ["l", "l", "l"]
>>> "hello world".scan("l") {|x| print x}
=> ["l", "l", "l"]

In contrast, CRuby gives this:
irb(main):001:0> "hello world".scan("l")
=> ["l", "l", "l"]
irb(main):002:0> "hello world".scan("l") {|x| print x}
lll=> "hello world"

Am I doing something wrong, or is this a bug?  (I have obviously updated Initializer.Generated.cs, or neither scan would have been found :).

2) My implementation of String.scan <regexp>, <block> is incomplete.  This function is defined to behave as follows:
   a.scan(/\w+/) {|w| print "<<#{w}>> " }
   a.scan(/(.)(.)/) {|x,y| print y, x }

In other words, the number of parameters being passed to the block is equal to the number of groups defined in the regular expression -- or 1 if there are no groups defined.  I haven't been able to find way to pass parameters  or define a call site or that would support this.


Finally, it's a bit annoying to rebuild Initializer.Generated.cs.  Whenever you change a method signature, you have to manually delete the appropriate part of the old file in order to regenerate it, or you'll get an error.  I've made an empty version of that source file and a batch file that copies it on top of the previous version before rebuilding ClassInitGenerator.  Assuming that the architecture is going to be here for a while, it would be nice if there were a target in the Rakefile that performed these steps.

After a few more hours of this, I may have to figure out how to do that myself. :)

--
Curt Hagenlocher
curt at hagenlocher.org<mailto:curt at hagenlocher.org>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20071013/d69149b6/attachment-0001.html 


More information about the Ironruby-core mailing list