BT

Pattern Matching in .NET 4

by Jonathan Allen on May 28, 2009 |

Case statements can be thought of as specialized version of the if/else syntax. There is nothing you couldn’t already do with them, and yet there are times when they seem much clearer. Consider this example in C# and VB.

double CaclRateByDate(DayOfWeek day)
  {
      if (day == DayOfWeek.Monday)
      {
          return .42;
      }
      else if (day == DayOfWeek.Tuesday)
      {
          return .67;
      }
      else if (day == DayOfWeek.Wednesday)
      {
          return .56;
      }
      else if (day == DayOfWeek.Thursday)
      {
          return .34;
      }
      else if (day == DayOfWeek.Friday)
      {
          return .78;
      }
      else if (day == DayOfWeek.Saturday)
      {
          return .92;
      }
      else if (day == DayOfWeek.Sunday)
      {
          return .18;
      }
      throw new ArgumentOutOfRangeException("Unexpected enum value");
  }
Function CaclRateByDate(ByVal day As DayOfWeek) As Double
    If day = Monday Then
        Return 0.42
    ElseIf day = Tuesday Then
        Return 0.67
    ElseIf day = Wednesday Then
        Return 0.56
    ElseIf day = Thursday Then
        Return 0.34
    ElseIf day = Friday Then
        Return 0.78
    ElseIf day = Saturday Then
        Return 0.92
    ElseIf day = Sunday Then
        Return 0.18
    Else
        Throw New ArgumentOutOfRangeException("Unexpected enum value")
    End If
End Function

Over and over again the developer is expected to write “ElseIf day =” or “else if (day ==” even though it doesn’t add any additional information. It is just noise that distracts from the important information, namely the day of the week and and the returned value.

 In both VB and C#, we can thin this quite a bit by using a case statement.

double CaclRateByDate2(DayOfWeek day)
{
    switch (day)
    {
        case DayOfWeek.Monday:
            return .42;
        case DayOfWeek.Tuesday:
            return .67;
        case DayOfWeek.Wednesday:
            return .56;
        case DayOfWeek.Thursday:
            return .34;
        case DayOfWeek.Friday:
            return .78;
        case DayOfWeek.Saturday:
            return .92;
        case DayOfWeek.Sunday:
            return .18;
        default:
            throw new ArgumentOutOfRangeException("Unexpected enum value");
    }
}
Function CalcRateByDate2(ByVal day As DayOfWeek) As Double
    Select Case day
        Case Monday
            Return 0.42
        Case Tuesday
            Return 0.67
        Case Wednesday
            Return 0.56
        Case Thursday
            Return 0.34
        Case Friday
            Return 0.78
        Case Saturday
            Return 0.92
        Case Sunday
            Return 0.18
        Case Else
            Throw New ArgumentOutOfRangeException("Unexpected enum value")
    End Select
End Function

But even with this, there is still a lot of code that just doesn’t need to be there. Why do you have to keep repeating the fact that you were returning a value? Wouldn’t it be nice if you could write something like this?

double CaclRateByDate2(DayOfWeek day)
{
    return switch (day)
    {
        DayOfWeek.Monday: .42;
        DayOfWeek.Tuesday: .67;
        DayOfWeek.Wednesday: .56;
        DayOfWeek.Thursday: .34;
        DayOfWeek.Friday: .78;
        DayOfWeek.Saturday: .92;
        DayOfWeek.Sunday: .18;
        default:
            throw new ArgumentOutOfRangeException("Unexpected enum value");
    }
}
Function CalcRateByDate2(ByVal day As DayOfWeek) As Double
    Return Select Case day
        Monday: 0.42
        Tuesday: 0.67
        Wednesday: 0.56
        Thursday: 0.34
        Friday: 0.78
        Saturday: 0.92
        Sunday: 0.18
        Case Else
            Throw New ArgumentOutOfRangeException("Unexpected enum value")
    End Select
End Function

When you eliminate the stuff you don’t actually care about, C# and VB look remarkably similar. All that is left is the pattern you looking for and the result that you want when that pattern is found. This is what is known as Pattern Matching.

 Well unfortunately we aren’t going to see something like this in C# 4 or VB 10. But there is a new language on the block that has support for pattern matching. Consider this F# example from Mathew Podwysocki. (Please note that in all these examples a function is being created.)

let calcRateByDay2 (day:System.DayOfWeek) = 
  match day with 
  | System.DayOfWeek.Monday -> 0.42 
  | System.DayOfWeek.Tuesday -> 0.67 
  | System.DayOfWeek.Wednesday -> 0.56 
  | System.DayOfWeek.Thursday -> 0.34 
  | System.DayOfWeek.Friday -> 0.78 
  | System.DayOfWeek.Saturday -> 0.92 
  | System.DayOfWeek.Sunday -> 0.18 
  | _ -> failwith "Unexpected enum value" 

In another example, Mathew shows how to multiple parameters can be checked at the same time. In this example, the underscore is used as a wild card.

let allowUrl url port =
  match (url, port) with
  | "http://www.microsoft.com/", 80 -> true
  | "http://example.com/", 8888 -> true
  | _, 80 -> true
  | _ -> false

Unfortunately F# doesn’t have a lock on concise syntax. If you need to do anything interesting with a value then you have to go back to specifying it by name or by a placeholder.

let calcRateByDay3 (day:System.DayOfWeek) = 
  match day with 
  | x when x >= System.DayOfWeek.Monday && x <= System.DayOfWeek.Friday -> 0.42
  | System.DayOfWeek.Saturday -> 0.92 
  | System.DayOfWeek.Sunday -> 0.18 
  | _ -> failwith "Unexpected enum value"

let calcRateByDay3 (day:System.DayOfWeek) = 
  match day with 
  | _ when day >= System.DayOfWeek.Monday && day <= System.DayOfWeek.Friday -> 0.42
  | System.DayOfWeek.Saturday -> 0.92 
  | System.DayOfWeek.Sunday -> 0.18 
  | _ -> failwith "Unexpected enum value"

By comparison, here is Visual Basic’s syntax for ranges within a case statement.

Function CaclRateByDate3(ByVal day As DayOfWeek) As Double
    Select Case day
        Case Monday To Friday : Return 0.42
        Case Saturday : Return 0.92
        Case Sunday : Return 0.18
        Case Else
            Throw New ArgumentOutOfRangeException("Unexpected enum value")
    End Select
End Function

As you can see, each language in on the .NET platform has certain syntactical strengths that could be applied to the other languages without changing the language’s core nature.

Hello stranger!

You need to Register an InfoQ account or to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Tell us what you think

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread
Community comments

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Email me replies to any of my messages in this thread

Discuss

Educational Content

General Feedback
Bugs
Advertising
Editorial
InfoQ.com and all content copyright © 2006-2013 C4Media Inc. InfoQ.com hosted at Contegix, the best ISP we've ever worked with.
Privacy policy
BT