Search Eridotnet


Friday, September 26, 2008

Quick thoughts on .NET 1.x, 2.0, 3.0 and 3.5 (part 6a: LINQ, the journey to get there, and a dive into functional programming)

Hi! I'm sorry for the long absent of this series continuation. Before this, I have provided you a quick glimpse about concepts behind .NET type system. Now, I'll bring you the previous local variable type inference, method extension, and query comprehension (query syntax) in a "walkthrough" style of LINQ.

The journey to get there, the LINQ in C# 3.0 and VB.NET 9.0 is quite a long and full of what-the-heck-of-these roads.

LINQ actually contains:

  • Local variable type inference
  • Extension methods
  • Query comprehension
  • Some cool functional programming constructs, the Lambda expressions and closure
  • A cool subset of "dynamic" programming feature, expression tree

I don't know whether this fact is good or bad, but C# 3.0 and VB 9.0 have this interesting fact: these guys know how to "steal" the good things from outside of the realm of imperative programming and incorporating them into the language, without ever changing the CLR of .NET 2.0! 

Why? Let's see what do we have in C# 3.0 and VB 9.0:

  • Lambda expressions (from functional programming)
  • Closures (from functional programming)
  • Expression tree (from dynamic programming)

Now, let's see... Let's have a quick overview of functional programming.

Quick overview of functional programming

What is functional programming? According to many sources, including Eric Meijer of Microsoft, functional programming is programming with functions, in terms of mathematical functions.

What is a function in a math term, really?

A function is, a computational operation that can have input and return an output.

A simple example of this, a simple x multiply by 2 function that has parameter "x" and returns a result:

f(x) = x *2

This function means do operation multiplication by 2 with parameter x, then return y as the result. These parameter and result should be typed, since it has to be meaningful type.

Sometimes, the f(x) is transferred to another symbol such as y:

y = f(x) = x * 2 

This y, is simply a variable.

Again, y and x should be given a type, or simply put, a strongly typed. There were numerous debates about this, since dynamically typed languages such as Javascript and Ruby have these function notation but they are dynamically typed, not strongly typed.

Note: for more information about type system overview, please read my previous blog entry about type system.

Function notation can also be written like this:

y = f(x) -> x * 2

If you omit the parentheses, you can write:

y= f:x -> x * 2


f:x -> y

And it read: "f is a function of x to y that has operation of x times 2", but the expression below.

This notation is so close with Lambda calculus notation which forms the base of the Lambda expression in C# 3.0 and VB 9.0:

in C# 3.0:

y = f(x) => x * 2

in VB 9.0:

Dim y = Function(x) x * 2

Now, enter the world of functional programming. Functional programming is simply translating those notation into programming languages that functional such as Haskell, Scheme, and F#. Yes, we now have functional programming notation in C# and VB, but it has no type declaration. Why? Because the type of the return value and the parameter are inferred. The compiler simply translate the return type into the right hand expression.

This is why this is called type inference. The best part of this, type inference not just give you less noise about giving type declarations, but it makes your code less bloated, and then further expands into expressions and later into expression trees.

Note: for more information about what type inference is, please read my previous blog entry about type inference in C# 3.0 and VB 9.0.

Functional programming often discusses about avoiding global states and effects. What are states and effects, really?

States mean that the value of variables. and global states mean that these variables have values in context of global (public static in C#) that in functional programming tries to avoid. Why? Because variables when they're treated as globals, they will affect the whole function execution. These give the notions of side effects, such as the current time and date of system.

What the hell is this? Then why do I have to take care this?

Well, functional programming can’t really have the freedom of mutable and immutable states, and then effects or in term of functional programming, side effects.

Let’s say if I have:

f(x) = x * 2

If I have set the x parameter by 3, then I’ll get 6, since 3 *2 = 6. This function is a real function, since I gave 3 and I will always get 6 as a result. Every time I call this function, I will get the same result and it modifies nothing else.

That’s why this simple function is defined as a pure function, a function that has no side effect because it has no other modified result.

What if I have this:

f(x) = DateTime.Now.Second

Can you guarantee that you’ll always have the same result?

Then this function is not pure, and it can’t give the same result every time I call it. Why do I care about this? Because it’s simply this notion that functional programming always care about the states and effects, and it’s contrary with imperative programming that always embrace states and effects.

Then, what about states?

Imperative programming has a degree of freedom to deal with mutable states, in a simple term is simply a mutable object, the one we often use in C++, C#, VB, and Java. But in functional programming, a mutable object and immutable object are treated carefully.

The function above is a sample of changing global state. DateTime.Now is a public static definition, and any functions that evaluate this will also modify the value, since it gives you current date and time, and of course current date and time is always changing.

DateTime is not just one effect. In a real situation, these are effects:

  • Input Output (I/O), such as writing to and reading from files and console.
  • System state, such as date, time, and handles and other operation that depends on these such as randomize. And this is simply an I/O effects too.
  • Exceptions (since it halts and changes the execution)

Some of you may ask, if I can’t have these, then my program is somehow disabled. Since you can’t do anything exciting with your program. And then it’s not fun anymore since you can’t compete with imperative programming.

But functional programming handles these restrictions using monads and giving the notion of explicit declaration of effects throughout its codes.

Sadly enough, many experts out there embracing pure functional programming and tries to remove side effects completely. The best approach is taking the extreme of purity into a less pure by allowing side effects but you have to declare it explicitly. As Eric Meijer from Microsoft call this, a honest function.

So, instead of writing this function in C#:

int GetCurrentSecond()

you should state that this function is a side effect of I/O that also give you integer:

IO<int> GetCurrentSecond()

This is quite simple, isn’t it? :)

But in C# and VB, you don’t have the notion of declaring that this function may gives exception. You don’t have explicit notion such as “throws” in Java.

But, as I mention about “Monad”, what is this?

Next: more diving into functional programming: Monad, Lambda, Currying, and more!


Jarred said...

Good one. But what about real functional programming samples?

You should mention it also. At least in some other languages, F#?