Forum: IronRuby A nicer syntax for generic extension methods

Posted by Ryan Riley (Guest)
on 2010-02-01 20:21
(Received via mailing list)
I have been trying to figure out how to Rubify generic extension methods 
for
use with the likes of Rx, Open XML SDK, etc. Ivan went over it a bit 
with me
this weekend, but I'm still having difficulty including a module within 
a
.NET type. Is that even possible?

Given:
names = System::Collections::Generic::List.of(System::String).new
names.add 'Ryan'

Current syntax:
greetings = 
System::Linq::Enumerable.method(:select).of(System::String).call
{ |name| "Howdy, #{name}" }
# compare with greetings = names.map { |name| "Howdy, #{name}" }

Desired syntax:
greetings = names.linq_select { |name| "Howdy, #{name}" }

Granted, you might make a case for using the Ruby Enumerable methods 
over
Linq, but when it comes to using other libraries, the more Ruby-like 
syntax
would be preferred. In the interest of working out the preferred syntax, 
I
thought using System.Linq.Enumerable would be easiest.

Ivan provided the following:

module EnumerableExtensions

 def included(base)
 if base.respond_to? :to_clr_type
 @klass = base.to_clr_type
 end
 end

def linq_select(&b)
 System::Linq::Enumerable.method(:select).of(@klass).call &b
 end

end

The questions I'm not able to answer are:

   1. Can I somehow open up a .NET class, say
   System::Collections::Generic::List[T] and include the 
EnumerableExtensions?
   So far, I'm finding that's a no.
   2. How do I hook in the included(base) method above? I'm assuming 
that's
   a one-time call, but I don't see anywhere that it's called when a 
module is
   included. Do I need to use a before_filter or perform that action at 
the
   beginning of the linq_select method?


Thanks!

Ryan Riley

Email: ryan.riley@panesofglass.org
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/
Posted by Orion Edwards (Guest)
on 2010-02-02 03:56
(Received via mailing list)
IIRC you can open "concrete" generics, but not "open" ones: In plain 
english
this means you can add methods to List<string> but not List<T>.

This is essentially because List<T> isn't a real type in the CLR, it's
basically some metadata that can be used to build a real type when the T 
is
supplied.

You could as an alternative add methods to the underlying non-generic
IEnumerable interface, but then you'd have to do some run-time 
reflection to
figure out that your List is actually a List<string>... This is probably 
not
nice.

In theory when CLR4 lands and has support for co/contra variant 
generics,
List<object> should match List<string> and everything else, but I don't 
know
if IronRuby would also work for this?

Good luck
Posted by Tomas Matousek (Guest)
on 2010-02-02 08:45
(Received via mailing list)
Actually, you can add Ruby methods to List<T> ... IronRuby type system 
does some magic for you :):

>>> include System::Collections::Generic
=> Object
>>> List[Fixnum].included_modules
=> [System::Collections::Generic::List[T], 
System::Collections::Generic::IList[Fixnum], 
System::Collections::Generic::IList[T], 
System::Collections::Generic::ICollection[Fixnum], 
System::Collections::Generic::ICollection[T], 
System::Collections::Generic::IEnumerable[Fixnum], 
System::Collections::Generic::IEnumerable[T], 
System::Collections::IEnumerable, Enumerable, 
System::Collections::IList, System::Collections::ICollection, 
System::Collections::Generic, Kernel]

As you can see the List<> generic type definition is treated as a module 
that is mixed in each of its instantiations. Although there are no 
predefined methods on it you can open it and add some. First we need to 
get Ruby class for List<T>. If you index System.Collections.Generic.List 
by a fixnum instead of a class/module you'll get the generic definition 
of arity 1. Let's name it ListOfT:

>>> ListOfT = List[1]

And then we can open it up:

>>> module ListOfT
...   def size
...     count
...   end
... end
=> nil
>>> l = List[Fixnum].new
=> []
>>> l.add(1)
=> nil
>>> l.add(2)
=> nil
>>> l.size
=> 2

Tomas


From: ironruby-core-bounces@rubyforge.org 
[mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Orion Edwards
Sent: Monday, February 01, 2010 6:31 PM
To: ironruby-core@rubyforge.org
Subject: Re: [Ironruby-core] A nicer syntax for generic extension 
methods

IIRC you can open "concrete" generics, but not "open" ones: In plain 
english this means you can add methods to List<string> but not List<T>.

This is essentially because List<T> isn't a real type in the CLR, it's 
basically some metadata that can be used to build a real type when the T 
is supplied.

You could as an alternative add methods to the underlying non-generic 
IEnumerable interface, but then you'd have to do some run-time 
reflection to figure out that your List is actually a List<string>... 
This is probably not nice.

In theory when CLR4 lands and has support for co/contra variant 
generics, List<object> should match List<string> and everything else, 
but I don't know if IronRuby would also work for this?

Good luck
On Tue, Feb 2, 2010 at 7:52 AM, Ryan Riley 
<ryan.riley@panesofglass.org<mailto:ryan.riley@panesofglass.org>> wrote:
I have been trying to figure out how to Rubify generic extension methods 
for use with the likes of Rx, Open XML SDK, etc. Ivan went over it a bit 
with me this weekend, but I'm still having difficulty including a module 
within a .NET type. Is that even possible?

...

The questions I'm not able to answer are:

  1.  Can I somehow open up a .NET class, say 
System::Collections::Generic::List[T] and include the 
EnumerableExtensions? So far, I'm finding that's a no.
  2.  How do I hook in the included(base) method above? I'm assuming 
that's a one-time call, but I don't see anywhere that it's called when a 
module is included. Do I need to use a before_filter or perform that 
action at the beginning of the linq_select method?

Thanks!

Ryan Riley

Email: ryan.riley@panesofglass.org<mailto:ryan.riley@panesofglass.org>
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/
Posted by Orion Edwards (Guest)
on 2010-02-02 20:40
(Received via mailing list)
>>  If you index System.Collections.Generic.List by a fixnum instead of a
class/module you’ll get the generic definition of arity 1

Nice! I had no idea that feature existed
Posted by Ryan Riley (Guest)
on 2010-02-02 23:45
(Received via mailing list)
That's fantastic, Tomas, thanks! Is there any way to pass a block, 
lambda,
or Proc into the slot for the delegate, or perhaps a way to create a 
.NET
delegate (or Expression) from a block, lambda, or Proc?

Thanks,

Ryan Riley

Email: ryan.riley@panesofglass.org
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/


On Tue, Feb 2, 2010 at 1:36 AM, Tomas Matousek 
<Tomas.Matousek@microsoft.com
Posted by Ivan Porto Carrero (Guest)
on 2010-02-03 07:02
(Received via mailing list)
just pass your block to the constructor of a delegate and you should be 
good
to go

Action.new { more_work_here }
---
Met vriendelijke groeten - Best regards - Salutations
Ivan Porto Carrero
Blog: http://flanders.co.nz
Twitter: http://twitter.com/casualjim
Author of IronRuby in Action (http://manning.com/carrero)
Posted by Ryan Riley (Guest)
on 2010-02-03 16:24
(Received via mailing list)
Thanks, Ivan. That's awesome ... that's just like F#. I should have 
realized
it would be that simple. I'll post this to the Delegates section of the 
.NET
interop page on the wiki, since it currently doesn't exist.

Also, I noticed you alluded to something similar in IronRuby in Action 
where
you talk about LightSpeed, but I couldn't find anything in the MEAP copy 
I
have. If I am able to spin up a few LINQ samples (probably Rx, Pfx, 
and/or
XLinq), I'll shoot them your way, if you are interested.

Cheers!

Ryan Riley

Email: ryan.riley@panesofglass.org
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/


On Tue, Feb 2, 2010 at 11:40 PM, Ivan Porto Carrero <
Posted by Ryan Riley (Guest)
on 2010-02-03 16:36
(Received via mailing list)
Okay, just posted a rough first stab at the topic. This is all generic
delegates, too; I haven't bothered with plain old generics. :) Please 
let me
know if you have any feedback. I will add the stuff Tomas noted above 
when I
get a chance a little later.

http://ironruby.net/Documentation/.NET/Delegates
<http://ironruby.net/Documentation/.NET/Delegates>
Ryan Riley

Email: ryan.riley@panesofglass.org
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/
Posted by Ryan Riley (Guest)
on 2010-02-03 17:01
(Received via mailing list)
I just thought of something else. How do you deal with anonymous types 
in
IronRuby? We don't have the type name when writing the code. Do we have 
to
create at least a Ruby type for everything returned from an
Enumerable.Select? I suppose that's where the module wrapper over LINQ 
would
perhaps come in handy?

Ryan Riley

Email: ryan.riley@panesofglass.org
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/
Posted by Ryan Riley (Guest)
on 2010-02-03 17:07
(Received via mailing list)
Or is that not even an issue? I suppose if you are writing a LINQ 
statement
within IR, you won't ever create an anonymous type; you would rather 
create
a Ruby type, hash, etc. I suppose this would only be an issue if you had 
a
method returning an anonymous type, which is already a documented no-no, 
so
I will retract my previous question. :) Sorry for the brain-dump.

Ryan Riley

Email: ryan.riley@panesofglass.org
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/
Posted by Ivan Porto Carrero (Guest)
on 2010-02-03 18:02
(Received via mailing list)
For lightspeed I wrote an internal dsl that allows you to query 
similarly to
the regular api but no special module for linq stuff. I skipped linq
altogether didn't need it there.

so what I created there is (It's in the asp.net mvc chapter btw)

find_all User do
   where a(:name).like("ivan%") & a(:age) > 23
   order_by :name.desc
end

that may be wrong but it's close to something like that..
---
Met vriendelijke groeten - Best regards - Salutations
Ivan Porto Carrero
Blog: http://flanders.co.nz
Twitter: http://twitter.com/casualjim
Author of IronRuby in Action (http://manning.com/carrero)
Posted by Ryan Riley (Guest)
on 2010-02-03 18:16
(Received via mailing list)
On Wed, Feb 3, 2010 at 10:49 AM, Ivan Porto Carrero <
ivan@whiterabbitconsulting.eu> wrote:

>
> that may be wrong but it's close to something like that..
>

I like that syntax. It reminds me of F#'s computation expressions. I was
thinking something more along the lines of what
arel<http://m.onkey.org/2010/1/22/active-record-query-interface?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed:+monkeyonrails+(m.onkey+on+rails)>is
doing.

Ryan
Posted by Tomas Matousek (Guest)
on 2010-02-03 18:34
(Received via mailing list)
The LINQ experience should get better as soon as we improve our 
interaction with the type inference engine in DLR overload resolver. 
IronRuby currently doesn’t use its full power like IronPython does, so 
you need to explicitly specify more generic arguments that should be 
necessary.

Tomas

From: ironruby-core-bounces@rubyforge.org 
[mailto:ironruby-core-bounces@rubyforge.org] On Behalf Of Ivan Porto 
Carrero
Sent: Wednesday, February 03, 2010 8:49 AM
To: ironruby-core@rubyforge.org
Subject: Re: [Ironruby-core] A nicer syntax for generic extension 
methods

For lightspeed I wrote an internal dsl that allows you to query 
similarly to the regular api but no special module for linq stuff. I 
skipped linq altogether didn't need it there.

so what I created there is (It's in the asp.net<http://asp.net> mvc 
chapter btw)

find_all User do
   where a(:name).like("ivan%") & a(:age) > 23
   order_by :name.desc
end

that may be wrong but it's close to something like that..
---
Met vriendelijke groeten - Best regards - Salutations
Ivan Porto Carrero
Blog: http://flanders.co.nz
Twitter: http://twitter.com/casualjim
Author of IronRuby in Action (http://manning.com/carrero)


On Wed, Feb 3, 2010 at 4:01 PM, Ryan Riley 
<ryan.riley@panesofglass.org<mailto:ryan.riley@panesofglass.org>> wrote:
Thanks, Ivan. That's awesome ... that's just like F#. I should have 
realized it would be that simple. I'll post this to the Delegates 
section of the .NET interop page on the wiki, since it currently doesn't 
exist.

Also, I noticed you alluded to something similar in IronRuby in Action 
where you talk about LightSpeed, but I couldn't find anything in the 
MEAP copy I have. If I am able to spin up a few LINQ samples (probably 
Rx, Pfx, and/or XLinq), I'll shoot them your way, if you are interested.

Cheers!

Ryan Riley

Email: ryan.riley@panesofglass.org<mailto:ryan.riley@panesofglass.org>
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/

On Tue, Feb 2, 2010 at 11:40 PM, Ivan Porto Carrero 
<ivan@whiterabbitconsulting.eu<mailto:ivan@whiterabbitconsulting.eu>> 
wrote:
just pass your block to the constructor of a delegate and you should be 
good to go

Action.new { more_work_here }
---
Met vriendelijke groeten - Best regards - Salutations
Ivan Porto Carrero
Blog: http://flanders.co.nz
Twitter: http://twitter.com/casualjim
Author of IronRuby in Action (http://manning.com/carrero)



On Tue, Feb 2, 2010 at 11:38 PM, Ryan Riley 
<ryan.riley@panesofglass.org<mailto:ryan.riley@panesofglass.org>> wrote:
That's fantastic, Tomas, thanks! Is there any way to pass a block, 
lambda, or Proc into the slot for the delegate, or perhaps a way to 
create a .NET delegate (or Expression) from a block, lambda, or Proc?

Thanks,

Ryan Riley

Email: ryan.riley@panesofglass.org<mailto:ryan.riley@panesofglass.org>
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/

On Tue, Feb 2, 2010 at 1:36 AM, Tomas Matousek 
<Tomas.Matousek@microsoft.com<mailto:Tomas.Matousek@microsoft.com>> 
wrote:
Actually, you can add Ruby methods to List<T> … IronRuby type system 
does some magic for you ☺:

>>> include System::Collections::Generic
=> Object
>>> List[Fixnum].included_modules
=> [System::Collections::Generic::List[T], 
System::Collections::Generic::IList[Fixnum], 
System::Collections::Generic::IList[T], 
System::Collections::Generic::ICollection[Fixnum], 
System::Collections::Generic::ICollection[T], 
System::Collections::Generic::IEnumerable[Fixnum], 
System::Collections::Generic::IEnumerable[T], 
System::Collections::IEnumerable, Enumerable, 
System::Collections::IList, System::Collections::ICollection, 
System::Collections::Generic, Kernel]

As you can see the List<> generic type definition is treated as a module 
that is mixed in each of its instantiations. Although there are no 
predefined methods on it you can open it and add some. First we need to 
get Ruby class for List<T>. If you index System.Collections.Generic.List 
by a fixnum instead of a class/module you’ll get the generic definition 
of arity 1. Let’s name it ListOfT:

>>> ListOfT = List[1]

And then we can open it up:

>>> module ListOfT
...   def size
...     count
...   end
... end
=> nil
>>> l = List[Fixnum].new
=> []
>>> l.add(1)
=> nil
>>> l.add(2)
=> nil
>>> l.size
=> 2

Tomas


From: 
ironruby-core-bounces@rubyforge.org<mailto:ironruby-core-bounces@rubyforge.org> 
[mailto:ironruby-core-bounces@rubyforge.org<mailto:ironruby-core-bounces@rubyforge.org>] 
On Behalf Of Orion Edwards
Sent: Monday, February 01, 2010 6:31 PM
To: ironruby-core@rubyforge.org<mailto:ironruby-core@rubyforge.org>
Subject: Re: [Ironruby-core] A nicer syntax for generic extension 
methods

IIRC you can open "concrete" generics, but not "open" ones: In plain 
english this means you can add methods to List<string> but not List<T>.

This is essentially because List<T> isn't a real type in the CLR, it's 
basically some metadata that can be used to build a real type when the T 
is supplied.

You could as an alternative add methods to the underlying non-generic 
IEnumerable interface, but then you'd have to do some run-time 
reflection to figure out that your List is actually a List<string>... 
This is probably not nice.

In theory when CLR4 lands and has support for co/contra variant 
generics, List<object> should match List<string> and everything else, 
but I don't know if IronRuby would also work for this?

Good luck
On Tue, Feb 2, 2010 at 7:52 AM, Ryan Riley 
<ryan.riley@panesofglass.org<mailto:ryan.riley@panesofglass.org>> wrote:
I have been trying to figure out how to Rubify generic extension methods 
for use with the likes of Rx, Open XML SDK, etc. Ivan went over it a bit 
with me this weekend, but I'm still having difficulty including a module 
within a .NET type. Is that even possible?

...

The questions I'm not able to answer are:

  1.  Can I somehow open up a .NET class, say 
System::Collections::Generic::List[T] and include the 
EnumerableExtensions? So far, I'm finding that's a no.
  2.  How do I hook in the included(base) method above? I'm assuming 
that's a one-time call, but I don't see anywhere that it's called when a 
module is included. Do I need to use a before_filter or perform that 
action at the beginning of the linq_select method?

Thanks!

Ryan Riley

Email: ryan.riley@panesofglass.org<mailto:ryan.riley@panesofglass.org>
LinkedIn: http://www.linkedin.com/in/ryanriley
Blog: http://wizardsofsmart.net/
Twitter: @panesofglass
Website: http://panesofglass.org/

_______________________________________________
Ironruby-core mailing list
Ironruby-core@rubyforge.org<mailto:Ironruby-core@rubyforge.org>
http://rubyforge.org/mailman/listinfo/ironruby-core


_______________________________________________
Ironruby-core mailing list
Ironruby-core@rubyforge.org<mailto:Ironruby-core@rubyforge.org>
http://rubyforge.org/mailman/listinfo/ironruby-core


_______________________________________________
Ironruby-core mailing list
Ironruby-core@rubyforge.org<mailto:Ironruby-core@rubyforge.org>
http://rubyforge.org/mailman/listinfo/ironruby-core


_______________________________________________
Ironruby-core mailing list
Ironruby-core@rubyforge.org<mailto:Ironruby-core@rubyforge.org>
http://rubyforge.org/mailman/listinfo/ironruby-core


_______________________________________________
Ironruby-core mailing list
Ironruby-core@rubyforge.org<mailto:Ironruby-core@rubyforge.org>
http://rubyforge.org/mailman/listinfo/ironruby-core
Posted by Ivan Porto Carrero (Guest)
on 2010-02-03 18:45
(Received via mailing list)
hashes?

but may i ask why you're so hell-bent on linq?  Linq is a really bad 
match
to ruby with the expressions. it would require some significant amount 
of
work to get that to work correctly.  AFAIK there is no way in IronRuby 
to
build expressions except use the raw API, but nothing is in there yet 
that
makes this easier to do.

Anyway you can reuse anonymous types did you know that? Alex James 
learnt
the trick from Wes Dyer
http://blogs.msdn.com/alexj/archive/2007/11/22/t-castbyexample-t-object-o-t-example.aspx


---
Met vriendelijke groeten - Best regards - Salutations
Ivan Porto Carrero
Blog: http://flanders.co.nz
Twitter: http://twitter.com/casualjim
Author of IronRuby in Action (http://manning.com/carrero)
Posted by Ryan Riley (Guest)
on 2010-02-03 18:48
(Received via mailing list)
Will that be 1.0 or after?

Ryan Riley


On Wed, Feb 3, 2010 at 11:12 AM, Tomas Matousek <
Posted by Ryan Riley (Guest)
on 2010-02-03 19:19
(Received via mailing list)
On Wed, Feb 3, 2010 at 10:59 AM, Ivan Porto Carrero <
ivan@whiterabbitconsulting.eu> wrote:

> hashes?


Just an example, though perhaps not the best. :)

but may i ask why you're so hell-bent on linq?
>

I mostly want some way to use the Reactive Extensions for asynchronous
programming and OpenXML SDK for working with Office docs. In particular, 
I'm
thinking of working with IR on top of other C# and/or F# libraries which
might be using one or both of those libraries. If I were to write it in 
all
Ruby, I'd probably look at eventmachine for an event loop processor.

I'm definitely open to other solutions.


> Linq is a really bad match to ruby with the expressions. it would require
> some significant amount of work to get that to work correctly.  AFAIK there
> is no way in IronRuby to build expressions except use the raw API, but
> nothing is in there yet that makes this easier to do.
>

L2S uses expressions, but I don't think everything uses them. If all 
that's
required is a delegate, that's not so hard and can be be accomplished 
with a
wrapping Ruby method that accepts a block and wraps the block into a 
Func to
pass into the LINQ method. Given some of the previous posts, I think
creating a general module to wrap these methods shouldn't be too 
difficult.
Thoughts?

Anyway you can reuse anonymous types did you know that? Alex James 
learnt
> the trick from Wes Dyer
>
> http://blogs.msdn.com/alexj/archive/2007/11/22/t-castbyexample-t-object-o-t-example.aspx
>

Yeah, I knew about that, but I don't really want to touch casting by 
example
in Ruby. It might not be bad, but why bother when I can use something 
from
Ruby?

Ryan
Posted by Miguel Madero (Guest)
on 2010-02-03 21:39
(Received via mailing list)
This might help, there's a library in the VS directory
samples/CSharpSamples.zip/LinqSamples/DynamicQuery take a look, under 
the
covers it's creating expressions LINQ Expressions.

In C# you will just chain methods like:
myCollection.Where("property=myValue").OrderBy("anotherProp desc");

 I think this library could be extended to "easily" create any type of
expression in IR. This could be useful to consume existing libraries 
that
already expect an expression.
Please log in before posting. Registration is free and takes only a minute.
Existing account (Switch to SSL-encrypted connection)
NEW: Do you have a Google/GoogleMail or Yahoo account? No registration required!
Log in with Google account | Log in with Yahoo account
No account? Register here.