Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Language Envy - episode 0

Although C# is the language that I can call myself proficient enough to make a living these days, there are other languages that I have to use for specific tasks (like JavaScript, SQL, XSLT.) I also like using other general purpose languages for pure exploration or pet projects. I'd include Ruby, ObjectiveC and PHP in this group.

When using other languages it often happens that I encounter features that I wish C# had or that the C#-equivalent was as easy (it works both ways — I miss some C# feature on the other side as well.)

In this series of undetermined length I will be posting some of the items from my wish list as I remember them.

The case statement

To start things off, let's check out C#'s case statement, straight from the language specification.

switch-statement:
    switch   (   expression   )   switch-block
switch-block:
    {   switch-sectionsopt   }
switch-sections:
    switch-section
    switch-sections   switch-section
switch-section:
    switch-labels   statement-list
switch-labels:
    switch-label
    switch-labels   switch-label
switch-label:
    case   constant-expression   : // <-- line 14
    default   :

I know that doesn't look like C# code. What I'd like to point is in line 14. The expression in each case label has to be a constant. I'm sure that helps making the switch statement compile to a very efficient MSIL code, but let's consider what we are missing because of that.

Here's a sample of what you can do in a Ruby case expression.

SIDE NOTE: The hawk-eyed reader will catch the terminology difference here. Many language constructs that are mere statements in C# are expressions in Ruby. But that's not the feature I'll write about today. Maybe in a future installment.
Months = %w(JAN FEB MAR APR MAY\
        JUN JUL AGO SEP OCT NOV DEC)

def get_month(value)
  case value
    when Date # class name (instance of?)
      return Months[value.month - 1]

    when /\d{4}-(\d{2})-\d{2}/ # Regular expression (matches ?)
      return Months[$1.to_i  - 1]

    when 1..12  # Range of values (contained ?)
      return Months[value - 1]

  end
end

puts get_month(Date.today)
puts get_month("2008-10-20")
puts get_month(8)

As you can hopefully see in the above example, the expressions in each when statement do not need to be constants (class names like Date are constants, by the way)

Ruby defines the === (triple equal) comparison operator that can be overriden in each class and is used in the case expression to test each when condition. This is usually read as "when value matches with this expression here...".

Not surprisingly, the built-in classes in Ruby do override the triple equal operator to add a more meaningful implementation for it. Range matches the values that are within the range. RegExp matches values that agree with the regular expression, Class objects match values that are instances of that class, etc.

I use this feature all the time and it's so convenient that I'd be thrilled to see it in C# one day.

So, what is my suggestion?

I wouldn't be a real programmer if I didn't try to sell my own suggestion, would I? Since IComparable is taken and means something different, I was thinking of maybe something like this.

public interface ICanMatch
{
  bool Match(object value);
}

public interface ICanMatch<T>
{
  bool Match(T value);
}
// I know, I know. That interface name doesn't have much of a chance
// of sticking. But, hey, that's _my_ wish list ;).

The C# compiler would check if any of the case labels uses an ICanMatch expression and invoke its Match method passing the switch value. If it returns true we have a match and the case's code block is executed.

Another alternative would be to add a new method to the System.Object that defaults to simply calling Equals. The thought of adding a new method there is a little bit too frightening for me.

At any rate, I can only dream of the day I'd be able to write:

public string GetMonth(dynamic value)
{
	switch(value)
	{
		case typeof(DateTime):
			return value.ToString("MMM");
		case new Regex(@"\d{4}-\d{2}-\d{2}"):
			return DateTime.Parse(value).ToString("MMM");
		case new MyRange(1, 12):
			return new DateTime(2000, value, 1).ToString("MMM");
	}
	return null;
}

This example is too simple and obviously contrived (a few method overloads or plain polymorphism would have taken care of that,) but I still hope you can see the value of this suggestion.


Posted 12-24-2008 4:32 PM by sergiopereira
Filed under: , , ,

[Advertisement]

Comments

Dew Drop - Xmas Edition - December 24-25, 2008 | Alvin Ashcraft's Morning Dew wrote Dew Drop - Xmas Edition - December 24-25, 2008 | Alvin Ashcraft's Morning Dew
on 12-25-2008 9:37 PM

Pingback from  Dew Drop - Xmas Edition - December 24-25, 2008 | Alvin Ashcraft's Morning Dew

Steve Gilham wrote re: Language Envy - episode 0
on 12-26-2008 2:16 AM

There have been a number of implementations of something quite similar posted this year, inspired by the pattern matching capabilities of functional programming languages (in particular by the rising profile of F# in the .Net world), and using a fluent interface style (after the style of LINQ).  The two most easily found are

weblogs.asp.net/.../functional-c-pattern-matching.aspx

bartdesmet.net/.../pattern-matching-in-c-part-8.aspx (and the previous 7 parts)

There are  a few inevitable differences from the features that Ruby offers -- at least, this side of C#4, we don't have dynamic types, so the patterns must have a common base type; and because C# does not treat System.Void as a type, unlike Ruby's Nil class.

sergiopereira wrote re: Language Envy - episode 0
on 12-26-2008 8:57 AM

@Steve, interesting posts. I had seen one of them before but did not think much of it.

They kind of prove my point that the switch statement can be improved. The fact that people are taking it on their own hands, and trying to circumvent the language limitation with fluent APIs is not surprising. I've written about that before:  devlicio.us/.../the-fluent-interface-or-dsl-conundrum.aspx

Dirk wrote re: Language Envy - episode 0
on 12-27-2008 4:16 PM

If that is what you're looking for, F# pattern matching will blow you off your socks :-)

diditwith.net/.../WhyILoveFPatternMatching.aspx

www.infoq.com/.../Beyond-Foundations-FSharp

sergiopereira wrote re: Language Envy - episode 0
on 12-27-2008 5:43 PM

@Dirk, moving to F# will not kill my language envy :) I'm stubborn and I wish I had that kind of thing in C# too.

About The CodeBetter.Com Blog Network
CodeBetter.Com FAQ

Our Mission

Advertisers should contact Brendan

Subscribe
Google Reader or Homepage

del.icio.us CodeBetter.com Latest Items
Add to My Yahoo!
Subscribe with Bloglines
Subscribe in NewsGator Online
Subscribe with myFeedster
Add to My AOL
Furl CodeBetter.com Latest Items
Subscribe in Rojo

Member Projects
DimeCasts.Net - Derik Whittaker

Friends of Devlicio.us
Red-Gate Tools For SQL and .NET

NDepend

SlickEdit
 
SmartInspect .NET Logging
NGEDIT: ViEmu and Codekana
LiteAccounting.Com
DevExpress
Fixx
NHibernate Profiler
Unfuddle
Balsamiq Mockups
Scrumy
JetBrains - ReSharper
Umbraco
NServiceBus
RavenDb
Web Sequence Diagrams
Ducksboard<-- NEW Friend!

 



Site Copyright © 2007 CodeBetter.Com
Content Copyright Individual Bloggers

 

Community Server (Commercial Edition)