

Why I Like Ruby, Part 2: Blocks - troygoode
http://blog.wekeroad.com/thoughts/why-i-like-ruby-blocks

======
troygoode
I too love Ruby and their blocks, but I do have to disagree with Rob's
statement that C#'s "block" syntax is "obtuse." Certainly it was in the days
of straight-up delegates, but the additions of Func<T>, Action, and anonymous
methods have really cleaned that up and made it nice and easy to use. Also,
I'm not entirely certain what Rob meant by calling LINQ a "Tinker-Toy syntax".

C# example of blocks (loosely based upon Rob's examples):

    
    
      //the action argument is the C# version of a block
      // - by specifying Action<Artist> I'm stating that the block
      // - will take 1 parameter, of type Artist
      public void ParseKey(XDocument doc,
                           string elName,
                           Action<Artist> block)
      {
        //using XLinq instead of Nokogiri... *jealous*
        foreach(var node in doc.Elements(elName))
        {
          var artist = new Artist{
            Name = node.Element("Name").Value,
            ArtistId = (int)node.Element("ArtistId").Value
          };
          block(artist);
        }
      }
    
      //called by:
      public void Main()
      {
        var doc = XDocument.Load("..\db\artists.xml");
        ParseKey(doc, "Artist", artist=>{
          //this is an anonymous method, the equiv
          // of a Ruby block. "artist=>" is the equiv
          // of |artist|
          Db.Save(artist);
          Console.WriteLine(artist.Name);
        });
      }
    

Certainly it is a bit cleaner in Ruby by way of:

1) Nokogiri is super awesome.

2) Ruby's dynamicness means we don't have to have type ceremony everywhere.

That said, they're pretty equivalent in terms of functionality and
expressiveness to me.

~~~
robconery
The "Tinker toy" reference was with respect to anonymouse delegates:

var thing () => {//code}

I think Anders is going to run out of symbols soon :).

The code you write here is fine - but you're tying Artist to the ParseKey
method - which is the point of doing the block in the first place (to free
that up). This might not look obtuse to you - but it's a lot more code than I
have :).

Also - please don't forget the tie-in with method_missing; the reason it's
nice is you're freeing your calling code from understanding the parser - which
I don't think you can do here...

~~~
troygoode
While the lambda pointer is certainly not the prettiest thing in the world,
"x=> { CODE }" isn't all that different from "do |x| CODE end". You might
prefer one over the other - and perhaps the Ruby one is easier to learn if you
learn it first - but I don't think there is a big difference.

On the other hand, you're certainly right that the code I wrote is longer than
yours. You are also correct that it ties itself to the Artist class, but that
was because I was only exercising the use of C#'s version of blocks - not
dynamic awesomesauce like method_missing. Method_missing is also possible to
do in C# with .Net 4, as I'm sure you know.

A more complete rewrite of your example might be along the lines of:

    
    
      //have to inherit from DynamicObject to get special dynamic treatment
      public class DynamicXmlElement : DynamicObject
      {
        private XElement _element;
    
        public DynamicXmlElement(XElement element)
        {
          _element = element;
        }
    
        //C# 4 equiv of method_missing
        public override bool TryGetMember(GetMemberBinder binder, out object result)
        {
          if(_element.Element(binder.Name) == null)
            return false;
          result = _element(binder.Name).Value;
          return true;
        }
      }
    
      //changed this to use Action<dynamic>
      public void ParseKey(XDocument doc, string elName, Action<dynamic> block)
      {
        foreach(var node in doc.Elements(elName))
          block(new DynamicXmlElement(node));
      }
    
      public void Main()
      {
        var doc = XDocument.Load("..\db\music.xml");
    
        ParseKey(doc, "Artist", artist=>{
          Db.SaveArtist(artist.ArtistId, artist.Name);
          Console.WriteLine(artist.Name);
        });
    
        ParseKey(doc, "Genre", genre=>{
          Db.SaveGenre(genre.GenreId, genre.Name);
          Console.WriteLine(genre.Name);
        });
      }
    

That said, yes, you can do this with less work/ceremony in Ruby as you've
shown. I'm a big fan of Ruby and I'm glad you're showing MS devs what makes it
so cool. I just don't want anyone to get the impression that some of these
basic things are things that simply cannot be done in C#.

~~~
robconery
Unfortunately no matter how many times I write that "you can do this in
[LANGUAGE]" - people don't see that part :). It's no the doing of it - it's
the doing of it well. Cleanly and expressively - that's what I like about
Ruby.

You have to see in the code you write here...

