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.scanon 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 -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20071013/07b3b822/attachment.html
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
Hi Curt, Sounds like a method binder bug. The workaround is to merge the two methods into one: [RubyMethod("scan", RubyMethodAttributes.PublicInstance)] public static object Scan(CodeContext/*!*/ context, MutableString/*!*/ self, MutableString searchStr, BlockParam block) { if (block == null) { // block wasn''t supplied } else { // ... } } Even though a "BlockParam" argument is present, you can still call the method w/o a block. (Essentially, all "BlockParams" are [Optional]) In the meantime, I''ll see if I can figure out why the binder is picking the wrong overload. - John 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/e14829f7/attachment.html
Whoops, looks like emails crossed. I think Tomas is right-- we''re not picking the block overload because the block parameter needs to be after self, not at the end like you might expect. Well, it''s worth trying to see if that fixes the problem :) Cheers, John From: John Messerly Sent: Saturday, October 13, 2007 11:14 PM To: ironruby-core at rubyforge.org Subject: RE: [Ironruby-core] Experimentation Hi Curt, Sounds like a method binder bug. The workaround is to merge the two methods into one: [RubyMethod("scan", RubyMethodAttributes.PublicInstance)] public static object Scan(CodeContext/*!*/ context, MutableString/*!*/ self, MutableString searchStr, BlockParam block) { if (block == null) { // block wasn''t supplied } else { // ... } } Even though a "BlockParam" argument is present, you can still call the method w/o a block. (Essentially, all "BlockParams" are [Optional]) In the meantime, I''ll see if I can figure out why the binder is picking the wrong overload. - John 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/2ea60967/attachment-0001.html
Thanks for all of the excellent advice; everything is now working. I had guessed there might be an issue on the overload with the specific signature of the function, but I wasn''t able to find any examples of existing code that contradicted what I had done. Now that I know what to look for, of course... :). I wonder if it wouldn''t be a good idea for the ClassInitGenerator to emit a warning when there''s a signature containing a BlockParam that doesn''t follow the correct pattern. The /*!*/ annotation seems a bit redundant with [NotNull], doesn''t it? I think I''m just resentful that it''s a bunch of characters that totally interrupt the flow of my typing. :P I considered merging the block form of the scan function with the blockless form, but it seemed that the result would be considerably harder to read and understand than keeping them seperate. Is this the sort of change you''d like to see submitted to the project? If so, I''ll write some tests (I know; should have done it the other way around) and generate another patch file. On 10/13/07, Tomas Matousek <Tomas.Matousek at microsoft.com> wrote:> > 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 { 2ndoverload 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 > > _______________________________________________ > Ironruby-core mailing list > Ironruby-core at rubyforge.org > http://rubyforge.org/mailman/listinfo/ironruby-core > >-------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20071014/29b798bd/attachment.html
Inline. From: ironruby-core-bounces at rubyforge.org [mailto:ironruby-core-bounces at rubyforge.org] On Behalf Of Curt Hagenlocher Sent: Sunday, October 14, 2007 6:52 AM To: ironruby-core at rubyforge.org Subject: Re: [Ironruby-core] Experimentation Thanks for all of the excellent advice; everything is now working. I had guessed there might be an issue on the overload with the specific signature of the function, but I wasn''t able to find any examples of existing code that contradicted what I had done. Now that I know what to look for, of course... :). I wonder if it wouldn''t be a good idea for the ClassInitGenerator to emit a warning when there''s a signature containing a BlockParam that doesn''t follow the correct pattern. Yes, that''s the plan - CIG will check for other errors as well. The /*!*/ annotation seems a bit redundant with [NotNull], doesn''t it? I think I''m just resentful that it''s a bunch of characters that totally interrupt the flow of my typing. :P Yes, you''re right it seems redundant but unfortunately it''s not. The problem is that our (i.e. DLR''s) NotNull attribute is currently not understood by Spec# (static verifier). It''s only influencing the overload resolution, nothing else. So Spec# would complain that you''re e.g. "dotting thru" a variable that might be null. If you can define a macro in your favorite text editor, this is the right use of that feature. I considered merging the block form of the scan function with the blockless form, but it seemed that the result would be considerably harder to read and understand than keeping them seperate. Is this the sort of change you''d like to see submitted to the project? If so, I''ll write some tests (I know; should have done it the other way around) and generate another patch file. Yes, we accept contributions to libraries, i.e. to IronRuby.Libraries.dll. Although MutableString class will stay where it is, Ruby method implementations for MutableString will soon get factored out to the Libraries assembly. Hence we could take your contribution then if it will be correct and efficient. Tomas On 10/13/07, Tomas Matousek <Tomas.Matousek at microsoft.com<mailto:Tomas.Matousek at microsoft.com>> wrote: 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) { 1 st 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> [mailto: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<mailto: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> _______________________________________________ Ironruby-core mailing list Ironruby-core at rubyforge.org<mailto:Ironruby-core at rubyforge.org> http://rubyforge.org/mailman/listinfo/ironruby-core -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20071014/0fe7e0a8/attachment-0001.html
On 10/14/07, Tomas Matousek <Tomas.Matousek at microsoft.com> wrote:> Yes, you''re right it seems redundant but unfortunately it''s not. The > problem is that our (i.e. DLR''s) NotNull attribute is currently not > understood by Spec# (static verifier). It''s only influencing the overload > resolution, nothing else. So Spec# would complain that you''re e.g. > "dotting thru" a variable that might be null. >Ah, I didn''t realize there was a program looking directly at the source code.> If you can define a macro in your favorite text editor, this is the > right use of that feature. >:)> Yes, we accept contributions to libraries, i.e. to > IronRuby.Libraries.dll. Although MutableString class will stay where it > is, Ruby method implementations for MutableString will soon get factored out > to the Libraries assembly. Hence we could take your contribution then if it > will be correct and efficient. >I see that the operations have now been factored out into MutableStringOps.cs, presumably in preparation for this migration. If it''s not premature, there''s an updated patch file (is that the preferred format?) at http://hagenlocher.org/software/MutableStringOps.scan.patch.txt which incorporates implementations for scan, upto, swapcase and swapcase!, as well as tests for all of these changes in test.string.rb. These days, i18n is at the top of my brain as my company works on internationalizing its product. It''s charmingly quaint to see that IronRuby (and presumably Ruby) have explicit tests against the ranges ''A''-''Z'' and ''a''-''z'' for purposes of checking and changing the case of individual letters :). -- Curt Hagenlocher curt at hagenlocher.org -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20071014/ba693b94/attachment.html
On 10/14/07, Curt Hagenlocher <curt at hagenlocher.org> wrote:> If it''s not premature, there''s an updated patch file (is that the > preferred format?) at > http://hagenlocher.org/software/MutableStringOps.scan.patch.txt which > incorporates implementations for scan, upto, swapcase and swapcase!, as well > as tests for all of these changes in test.string.rb. >Proving that "you can''t eat just one", I''ve amended this to include center, chomp, chomp!, chop and chop!. And they say that watching television isn''t productive... -- Curt Hagenlocher curt at hagenlocher.org -------------- next part -------------- An HTML attachment was scrubbed... URL: http://rubyforge.org/pipermail/ironruby-core/attachments/20071014/38732aac/attachment.html