Inside F#

Brian's thoughts on F# and .NET

Gotcha explained

Posted by Brian on January 28, 2011

Last time I posted a “gotcha” snippet, asking what this F# code prints:

    type MyVector(x:int, y:int, z:int) =

        // expose the values as an array
        member this.Values = [| x; y; z |]

    let v = new MyVector(1,2,3)
    v.Values.[0] <- 42
    printfn "%d" v.Values.[0]

It prints “1”.  To see why, let’s look at the corresponding C# code, in all its verbosity:

    class MyVector

    {
        int x, y, z;
        public MyVector(int x, int y, int z)
        {
            this.x = x;
            this.y = y;
            this.z = z;
        }
        // expose the values as an array
        public int[] Values
        {
            get { return new[] { x, y, z }; }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var v = new MyVector(1, 2, 3);
            v.Values[0] = 42;
            System.Console.WriteLine(v.Values[0]);
        }
    }

The C# code, being more explicit, makes things a little more obvious.  “Values” is a getter property, whose body creates a new array, and so every time you refer to “.Values”, a fresh array is created that contains the 3 integers that were passed into the constructor.  So the line that assigns the value 42 assigns it into a fresh array that is then immediately dropped on the floor.

This probably isn’t what is desired.  If we want “Values” to expose a single instance of a mutable array, then we can easily do so, by creating that array instance in the class constructor and returning that same instance each time people refer to the property.  In C#:

    class MyVector

    {
        int[] a;
        public MyVector(int x, int y, int z)
        {
            this.a = new[] { x, y, z };
        }
        // expose the values as an array
        public int[] Values
        {
            get { return a; }
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            var v = new MyVector(1, 2, 3);
            v.Values[0] = 42;
            System.Console.WriteLine(v.Values[0]);
        }
    }

and in F#:

    type MyVector(x:int, y:int, z:int) =

        let a = [| x; y; z |]
        // expose the values as an array
        member this.Values = a

    let v = new MyVector(1,2,3)
    v.Values.[0] <- 42
    printfn "%d" v.Values.[0]

I see people occasionally stumble on this in a variety of ways; the fact that F# property getter syntax (and overall syntax) is so succinct makes it easier to forget that the body of the member is code that will run each time the member is referenced.  One-time initialization should be moved to the constructor (the let/do section prior to the members; see this blog for a primer on the F# class/interface syntax).

Every language has its own little gotchas; at work those on the C# team still fall into the trap described here, and so it is fun to josh each other about the minor warts in our respective languages.  :)

About these ads

4 Responses to “Gotcha explained”

  1. We’ve made it a part of our coding standard to always use the long form of getter properties. The mistake you pointed out is just way too common among F# beginners. I’m still finding that mistake in our earlier code. Even if the copies don’t cause logic errors they can slow things down.

    // Our get properties look like this.
    member this.Values
        with get() =
            [| x; y; z |]

  2. Mark Rockmann said

    Your last F# definition still has a subtle issue. Each parameter of the primary constructor becomes a field, and each let binding becomes a field as well. The equivalent C# code is

        class MyVector {
            int x;
            int y;
            int z;
            int[] a;
            // ...
        }

    To avoid doubling the space requirements, use a private primary constructor.

        type MyVector private(a:int array) =
            member this.Values with get() = a
            new(x, y, z) = MyVector [| x; y; z |]
    • Brian said

      Mark, that’s unnecessary in F#. The F# compiler and language spec is smart here, and x/y/z are only local variables in the constructor, not instance fields. See section 8.6.2 of the F# language specification (“let” and “do” declarations in primary constructors) for details. (But it’s great to have blog readers who are scrutinizing things so carefully! And the private primary constructor technique you used is indeed sometimes useful in other circumstances.)

  3. […] Brian McNamara’s Gotcha explained “I see people occasionally stumble on this in a variety of ways; the fact that F# property getter syntax (and overall syntax) is so succinct makes it easier to forget that the body of the member is code that will run each time the member is referenced.  One-time initialization should be moved to the constructor (the let/do section prior to the members; see this blog for a primer on the F# class/interface syntax).” […]

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: