Inside F#

Brian's thoughts on F# and .NET

What does this C# code look like in F#? (part one: expressions and statements)

Posted by Brian on November 28, 2008

If you have C# code and just try to transliterate it directly to F#, the F# code you wind up with is unlikely to be idiomatic or "good".  Nevertheless, when you are new to a language (F#), it is sometimes useful to know how to transliterate from a well-known language (C#) for those cases where you just don’t know the idioms yet, but don’t want that to prevent you from making progress.  So today’s blog entry takes the general form of "here’s some C# code, how do I write the same thing in F#".  It is intended to be used as a reference or as casual reading to discover some little-used or lesser-known F# syntax/operators/functions.  Don’t use this blog post as a way to learn the language, instead use the many useful F# tutorials and samples on the web (start here), and just use this blog post to "fill in the gaps" if needed.

Today’s blog entry covers only code that goes inside a method body, so you won’t find anything about declaring classes or using namespaces here.  Instead I will be covering the following bits of C#:

  • casts
  • operators
  • expressions
  • statements

and show a way to transliterate those C# forms into the corresponding F#.  Along the way I will inject some editorial comments about what is good, bad, idiomatic, etc, but I will not go into much detail.  If I leave anything common out, or something is very unclear, feel free to post a question as a comment on this blog entry.

Without further ado!

Casts

The cast "operator" in C# (saying "(Typename)expr") can mean all kinds of different things in C#, depending on the source type and destination type.  I will just call out the four most common conversions done via casts:

    // C#
    char c = ‘a’;
    int x = (int)c;         // numerical conversion
    object o = (object)c;   // boxing conversion
    Dog d = new Dog();
    Animal a = (Animal)d;   // upcast
    d = (Dog)a;             // downcast (may fail at runtime)

Here is the corresponding F# code, followed by some commentary.

    // F#
    let c = ‘a’
    let x = int c        // numerical conversion
    let o = box c        // boxing conversion
    let d = new Dog()
    let a = d :> Animal  // upcast
    let d2 = a :?> Dog   // downcast (may fail at runtime)

Numerical conversions in F# are done via library functions.  For example, the function "int" converts a char (or a float or a uint or a whatever) into an int.  There are similar functions for each destination type, so for example the function "char" converts it argument to a char.  You can read a little more about these functions in the library reference.  Note that the "enum" function converts to an enumerated type; the specific enumeration type will be inferred, which sometimes requires a type annotation, as in

    let y : MyEnumType = enum 0 // F#: y is 0-value of MyEnumType

A boxing conversion turns a primitive value into an object.  The F# function "box" does this.  In fact, "box" will upcast anything to the object type System.Object (which in F# goes by the shorthand name "obj").

Upcast: If you want to make a conversion that goes up a class/interface hierarchy, use the F# syntax "expr :> type".  If this compiles, then this conversion will always succeed at runtime.  (Note: in F# 1.9.6.2, this expression form has somewhat weird precedence, and so you may sometimes need to surround it with parens, e.g. "(expr :> type)" to make the code parse.)  You rarely need to use this operator.

Downcast: If you want to make a conversion that goes down a class/interface hierarchy, use the F# syntax "expr :?> type".  The question mark inside this operator is meant to suggest that the operation may fail; just as in C#, you may get an InvalidCastException.  You should rarely use this operator; in C# you typically prefer "is" and "as", see those expressions below for the corresponding idiomatic F#.

Operators

Many arithmetic (e.g. +, *) and conditional (e.g. &&, ||) operators are the same in F# as in C#.  The most common places to get tripped up are (1) with logical negation: in C# it is spelled "!", whereas in F# it is spelled "not"; (2) with equality testing, in C# it is "==" whereas in F# it is just "="; and (3) with inequality, where C# has "!=" whereas F# has "<>".  (As for logical negation, note that "!" already has a meaning in F# associated with the "ref" type, which is inherited from OCaml, oh well.)  The other somewhat common place to get tripped up is that in C# the bitwise-logic operators are one character (e.g. "|" and "&"), whereas the F# operators are three characters ("|||" and "&&&"); you’ll need these operators when using e.g. "Flags" enums.

Most F# operators are overloaded, don’t be fooled by the type inference tooltips when hovering over code like this in Visual Studio:

let f x y = x + y  // hover mouse over f, says int -> int -> int

This does not mean that "+" only works on ints; you can use "+" to add two floats, or two chars, or even two strings.  (The mechanism by which this overloading works involves esoteric black magic involving "inline" and "^a" types.  You’ll be happier if you stay ignorant of those details; the language/tooltips pick "int" as a default when things are otherwise unconstrained in order to hide the underlying complexity.  (A much better "solution" to "how to overload common operators like ‘+’" is to use type classes, but neither the CLR nor even F# have sufficiently expressive type systems to handle type classes, oh well.))

In C#, the same operator (‘=’) is used for both initialization and destructive assignment:

    int i = 3;    // initialization
    i = 4;        // destructive assignment

In F#, these are separate operators (‘=’ and ‘<-’), and only mutable variables admit assignment:

    let i = 3    // initialization
    i <- 4       // does not compile, i is immutable
    let mutable j = 3
    j <- 4       // destructive assignment
 

Expressions

Most expressions involving constructing objects, methods, and properties look the same in C# and F#:

    new Dog()  // constructor
    s.StartsWith("h", StringComparison.Ordinal)  // method call
    s.Length   // property

One notable difference is the syntax for indexing of arrays or other types.  Whereas in C# you say:

    var dict = new Dictionary<string, int>();
    dict["foo"] = 42;
    Console.WriteLine(dict["foo"]);

in F# you need a dot (‘.’) before the square brackets:

    let dict = new Dictionary<string,int>()
    dict.["foo"] <- 42
    printfn "%d" dict.["foo"]

There are tons of variations of syntax for doing lambdas/delegates in C#, I’ll show only one:

    Func<int, int, int> f = (x, y) => x + y;  // C# lambda

and the roughly corresponding F#:

    let f = (fun x y -> x + y)  // F# lambda

There are lots of interesting differences between the language when it comes to representing lambdas/functions/delegates, and I will not go into any detail about these differences in today’s blog entry.

Both C# and F# have "typeof" that returns a System.Type, but C# uses round brackets whereas F# uses angle brackets:

    typeof(int// C# typeof
    typeof<int// F# typeof

Another distinction involves generic types; C# allows you to omit types to get the generic definition:

    Console.WriteLine(typeof(List<int>).IsGenericTypeDefinition); // false
    Console.WriteLine(typeof(List<>).IsGenericTypeDefinition);    // true

whereas F# has a separate operator called "typedefof" for getting uninstantiated generic types:

    printfn "%A" (typeof<List<int>>.IsGenericTypeDefinition)  // false
    printfn "%A" (typedefof<List<_>>.IsGenericTypeDefinition) // true

C# has "is" and "as" operators for type tests.  F# uses a particular pattern in a match for this.  So this C# code:

    if (animal is Dog)
    {
        Dog dog = animal as Dog;
        // …
    }
    else if (animal is Cat)
    {
        Cat cat = animal as Cat;
        // …
    }

becomes this F# code:

    match animal with
    | :? Dog as dog -> // …
    | :? Cat as cat -> // …

where ":? type" is a type test, and "as ident" names the resulting value of that type if the type test succeeds.  (One other aside: in F# "else if" can be abbreviated as "elif".)

C# has the ternary operator "?:" for conditional expressions:

    condition ? trueVal : falseVal

F# has the same operator, but its name is if-then-else:

    if condition then trueVal else falseVal

(Note that "if" is used much less frequently in F# than in C#; in F#, many conditional expressions are done via pattern-matching rather than if-then-else.)

C# has an operator called "default" that returns the zero-initialization value of a given type:

    default(int)

It has limited utility; most commonly you may use default(T) in a generic.  F# has a similar construct as a library function:

    Unchecked.defaultof<int>

These respective constructs are rarely used in C# and F#.

Statements

This section describes how to transliterate C# statements into F#.  F# doesn’t have "statements", per se; everything is just an expression, and evaluating a a method body just means evaluating its expressions.  The expressions that are most statement-like in F# just return "()", the sole value of the "unit" type, which is akin to "void" in C#.

C# has three looping constructs:

    // C# loops
    while (condition)
    {
        SomeCode();
    }
    foreach (var e in someEnumerable)
    {
        SomeCode();
    }
    for (int i = 0; i < 10; ++i)
    {
        SomeCode();
    }

F# has just "while" and a "foreach" (spelled "for" in F#); a C# "for" loop can usually be emulated with a "range":

    // F# loops
    while condition do
        SomeCode()
    for e in someEnumerable do  // foreach
        SomeCode()
    for i in 0..9 do  // compiles like a C# for loop
        SomeCode()

F# lacks anything like "break" or "continue"; you must use control flow and/or boolean flag variables to emulate these constructs.  "While" loops in F# are rare, since they imply mutable variables in the condition, and F# often eschews mutables, preferring recursion for simple loops.  For(each) loops are also somewhat rarer in F#, since sometimes the form

    someEnumerable |> Seq.iter (fun e -> SomeCode())

is preferred (especially when the argument to Seq.iter is short). 

C# has a switch statement.  It looks something like this:

    switch (x)
    {
        case 1:
            SomeCode();
            break;
        default:
            SomeCode();
            break;
    }

In F#, this is just one of many things that pattern matching expresses more succinctly:

    match x with
    | 1 -> SomeCode()
    | _ -> SomeCode()  // _ is a ‘catch all’ default

C# has a "return" keyword that exits the current function.  There is no comparable construct in F#.  Note that the "return" keyword in F# is part of computation expression syntax (a.k.a. "workflows", a.k.a. "monads") and has nothing to do with C#’s "return".  As with C#’s "break", in F# you will need to use control-flow constructs to simulate "return" for an early exit.  (Though I do occasionally wish for "break" in F#, I have never wished for "return" – I don’t recall ever needing/wanting it.)

In C#, you raise an exception with the "throw" keyword.  In F#, you use the function "raise". 

    throw new Exception("boom");    // C#
    raise <| new Exception("boom")  // F#

This is one of the few places in F# where I think it is very idiomatic to use the backward-pipeline operator "<|".  Without it, the precedence rules of F# force you to parenthesize the expression:

    raise( new Exception("boom") )  // F#

which I think most people feel looks awkward.

C# has a try-catch-finally construct for exception handling.  As a result you can write code like this:

    try
    {
        SomeCode();
    }
    catch (NullReferenceException nre)
    {
        // swallow it
    }
    catch (Exception e)
    {
        throw;
    }
    finally
    {
        SomeOtherCode();
    }

The equivalent F# code would be:

    try
        try
            SomeCode()
        with 
            | :? NullReferenceException as nre -> () // swallow it
            | e -> rethrow()
    finally
        SomeOtherCode()

There are a few things to point out here.  First, in F#, try-with and try-finally are separate constructs – there is no try-with-finally.  In practice, you usually only do one or the other (and you should probably be using try-finally about ten times as often as try-catch, in either language – catching exceptions is rarely the right thing to do).  F# "catch" blocks just use pattern matching type tests on the exception, much like we saw earlier with the transliteration of C# "is"/"as".  In F#, to re-throw the exception, use "rethrow()". 

C# has a "checked" statement for checking for arithmetic overflow; F# has checked operators in the library, so just open this module and use the operators there if you need overflow checking.

C# has a "lock" statement for locking an object for a critical section:

    lock (o)             // C# lock
    {
        SomeCode();
    }

In F#, "lock" is a function that takes the lock object and a lambda of the critical section:

    lock o (fun () ->    // F# lock
        SomeCode()
    )

C# has "using" for IDisposables:

    using (var disp = someDisposable)
    {
        SomeCode();
    }

F#’s corresponding syntax:

    use disp = someDisposable 
    SomeCode()

F#’s "use" is like "let", it is scoped to the end of the surrounding block.

Inside a C# method that returns an IEnumerable, the following "yield" statement forms are allowed:

    yield return 42// C# yield a value
    yield break;      // C# end the enumeration

In F#, you don’t have to declare a method-returning-IEnumerable to use "yield", instead you can make a sequence expression anywhere using "seq{…}":

    let myIntSeq : IEnumerable<int> = seq { yield 42; }

I added a type annotation just to remind you that "seq" in F# just means "IEnumerable".  So the sequence expression on the right-hand-side of the line of code above evaluates to an IEnumerable<int> that yields a single value (42).  As with a C# iterator block, you can put arbitrary code using yields in an F# sequence expression.  Just as with loops and C# "break", there is no "yield break" in F#.

The end

I think that just about covers all the interesting C# expressions and statements, showing the corresponding F# counterparts.  I hope this is a useful reference for filling in gaps in your F# knowledge while learning F#.

About these ads

6 Responses to “What does this C# code look like in F#? (part one: expressions and statements)”

  1. Sam said

    Great job!

  2. Pavel said

    > F# has just "while" and a "foreach" (spelled "for" in F#); a C# "for" loop can usually be emulated with a "range"That\’s incorrect, F# actually has a traditional Pascal-style for-loop: for i = 1 to 10 do SomeCode(i) for i = 10 downto 1 do SomeCode(i)

  3. Unknown said

    > there is no try-with-finallyI think F# designers repeat some of Python\’s design mistakes. This one was corrected in Python 2.6, so somebody really needed it.> In practice, you usually only do one or the otherAre you sure? Are you going to suggest removing it from C# too? Or are you just justifying this stupid design decision?

  4. Santiago said

    Great Job, I\’m learning F# from C# and your post is useful. Thanks

  5. chunmin said

    I really do not understand why F# introduces so many different syntax from C#?If similar sntax to c# is used for F#, what happens?so many abnormal operators adopted by F#!

  6. Robert said

    Done Right!

    Great job getting me into F#! This was about the most practical approach to get my mind wet with F# coming from a C# background. All the other theoretical explanations were nice but seeing one practice and how it’s done there just does the job.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
Follow

Get every new post delivered to your Inbox.

Join 30 other followers

%d bloggers like this: