The C# Light project
Welcome to the “C# light” project page.
As you have probably guessed, “C# light” is not a real project. It was inspired by this post by Phil Trelford.
However, the clean syntax and features of “C# light” are based closely on the F# language, which this site is devoted to.
Many people think that F# has a strange syntax, and are put off by the name. I thought that if I created a “C# light” slideshow that demonstrated the benefits of the F# syntax under another name, skeptical C# developers might be more open-minded!
So, if you are a skeptical C# developer, let me show you that F# is almost identical to “C# light”, with only a few minor tweaks.
Here is the “C# light” example that I used throughout the slides:
class Person(string name, DateTime birthday) =
/// Full name
member Name = name
/// Birthday
member Birthday = birthday
class Person(string name, DateTime birthday) =
/// Full name
member Name = name
/// Birthday
member Birthday = birthday
And here is the equivalent F# code:
type Person(name :string, birthday :DateTime) =
/// Full name
member this.Name = name
/// Birthday
member this.Birthday = birthday
There are a few minor syntax changes:
- The keyword
class
is replaced withtype
- The type annotations are “backwards”. Rather than
string name
, the parameter is declared asname:string
- The
this
keyword is added to the member declarations.
But other than that, the code is very similar.
Here is the example in the C# light slide deck which uses a syntax similar to anonymous types to declare a simple DTO class:
class Person = {string name, DateTime birthday}
var person = {name="Alice", birthday=Today}
And here is the F# equivalent:
type Person = {name :string; birthday :DateTime}
let person = {name="Alice"; birthday=DateTime.Today}
There are two more changes here:
- The
var
keyword is replaced bylet
in F#. - Commas are replaced by semicolons in both the definition and usage of the class.
As promised by the slides, the F# compiler does automatically generate equality code for most types.
type Person = {name :string; birthday :DateTime}
let alice1 = {name="Alice"; birthday=DateTime.Today}
let alice2 = {name="Alice"; birthday=DateTime.Today}
if alice1 = alice2 then
Console.WriteLine("Alice1 and Alice2 are equal") // true!
In F#, user-defined types are immutable by default. If you want to change them, you have to use the mutable
keyword.
type Person = {name :string; birthday :DateTime}
//define an immutable Alice
let alice3 = {name="Alice"; birthday=DateTime.Today}
//changing immutable Alice to Bob gives an error
alice3 <- {name="Bob"; birthday=DateTime.Today} // This value is not mutable
//define a mutable Alice
let mutable alice4 = {name="Alice"; birthday=DateTime.Today}
//changing mutable Alice to Bob is ok
alice4 <- {name="Bob"; birthday=DateTime.Today}
Console.WriteLine("Alice's name is " + alice4.name)
In F#, user-defined types are not allowed to be null.
type Person = {name :string; birthday :DateTime}
let mutable alice4 = {name="Alice"; birthday=DateTime.Today}
alice4 <- null // error
// The type 'Person' does not have 'null' as a proper value
Here’s how an object instance can implement an interface in F#, in this case IDisposable
.
let myFunction() =
// create a disposable with a "use" keyword
use tempDisposable =
{new IDisposable with
member this.Dispose() = Console.WriteLine("Disposed") }
// do something
Console.WriteLine("Doing something")
// tempDisposable goes out of scope and is disposed
The console output is:
Doing something
Disposed
In F#, these are actually called “object expressions”.
In F#, these are called “discriminated unions” and they are one of the best things about F#.
Here’s the C# light version:
class PaymentMethod =
| Cash
| Check(int checkNo)
| Card(string cardType, string cardNo)
And here’s the F# version:
type CheckNo = int
type CardType = Visa | Mastercard
type CardNo = string
type PaymentMethod =
| Cash
| Check of CheckNo
| Card of CardType * CardNo
Here’s the C# way of constructing an object:
PaymentMethod cash = Cash();
PaymentMethod check = Check(123);
PaymentMethod card = Card("Visa", "4012888888881881");
And here’s the F# way:
let cash = Cash
let check = Check 123
let card = Card (Visa, "4012888888881881")
Finally, here’s the C# way of deconstructing a payment:
void PrintPayment(payment) =
switch (payment)
{
case Cash : // print cash
case Check(checkNo) : // print check info
case Card(cardType,cardNo) // print card info
}
And here’s the F# way:
let printPayment payment =
match payment with
| Cash -> printfn "Cash"
| Check checkNo -> printfn "Check %i" checkNo
| Card (cardType,cardNo) -> printfn "Card %A %s" cardType cardNo
printPayment cash
printPayment check
printPayment card
The F# code for these examples is available on .Net Fiddle here. Do play with them to convince yourself that F# really does fulfill all the promises of C# Light!
If you want to see more about F#, this page is a good starting point for browsing this site. Or visit fsharp.org for more about F# in general.
And for more about the power of F# types, check out my slide show on “Domain Driven Design with F# types”, below. And there is more DDD stuff here.