Join Kit Eason for an in-depth discussion in this video Struct tuples, part of What's New in Visual Studio 2017 for F# For Developers.
- [Instructor] Okay, well let's dive straight in and look at the new ways that F Sharp 4.1 lets you control the way that values are stored. There are quite a few changes here. We've got struct tuples, which I'll explain in a moment. We can also make record structs. We can make unions into structs. All of those essentially are memory allocation and potentially performance changes. We've got the fixed keyword, which lets you pin a value at a particular location in memory and not have a garbage collector move it; and we've got ByRef returns, which is little changed to interact with C Sharp 7's ByRef concept.
And we're going to start with struct tuples. So, here we're looking at some F Sharp 4.1 code, but the first tuple you've declared on line six where I say my tuple equals brackets one two, we've got a completely ordinary pre-F Sharp 4.1 tuple. If you know F Sharp, you'll be very familiar with that construct; and we've got the print tuple function, which does a match statement, breaks a tuple down into two values on line 10, which we're calling A and B and prints them out in some desired format.
And then on line 12 we're calling that print tuple function giving the tuple we declared on line six as an argument. So let's go ahead and send that lot to F Sharp Interactive. I'll select it, hit Alt + Enter, and there we go. You can see one comma two, which is me declaring the my tuple value. We see a declaration of print tuple, which takes a tuple of int and int and, well, returns nothing; and actually if I just call print tuple once again, just to prove where the output was coming along, I could even change that to some other format if I wanted to.
Let's put a dash there, just so it's kind of not a default looking representation. There we go, call it again, one dash two. So, that was just by way of revision. That's completely standard pre-F Sharp 4.1 tuple handling. Well, how does that change when we make a tuple a struct? Well, I guess the first thing to say is why we'd want to do that; and the reason is because that will make the tuple a value type rather than a reference type. And if you remember your dot nets, you'll know that value types are allocated on the heap and reference types are allocated on the stack; and that can have implications for performance and memory allocation, stuff like that.
Certainly in simply scenarios you may well see a performance gain from using value types, although it's not guaranteed. So the only change we've had to make to make this a struct tuple is simply this keyword struct on line 15. We can send that to F Sharp Interactive, and you see at the bottom of F Sharp Interactive there, we see it says struct of int by int, not just a struct. So now what happens if we try and call print tuple on that? Well, bad things.
You see I'm calling print tuple on line 20 with my struct tuple. Just scroll up a little bit so you could see the declaration of struct tuple, and you see I'm getting an error. I hover over the error; that's saying one tuple type is a struct tuple, the other is a reference tuple. So the takeaway from that is these are different types even though their semantics are extremely similar and even though people often like to tell us that where it's allocated is an implementation detail. These are different types, and you know how strict F Sharp is about types.
How to deal with that? Well, luckily, just down here I've got print struct tuple on line 23; and that's very, very similar, except within the match statement we're saying, match X with struct A and B, so that makes the thing we're matching on the same type and all is good. So I'm going to edit this just a tiny bit to match what we did before. I'm going to make sure we've got the struct tuple in Interactive. I'm going to just recomment that out so it's not too distracting.
I'm going to define print struct tuple in F Sharp Interactive, and then I'm going to call that. And there we go; we've got our one minus two representation of the struct tuple. So, really easy takeaway there. We bind it using struct, and we parse it out, so to speak; we match it also using struct. Well, why would we do that? As I say, because it may have implications for performance and how heavy you are on the stack or not, as the case may be.
So here we've got some code which defines a value of 10 million and then goes ahead and creates an array of 10 million simple tuples, and each tuple contains i and i times i; and down here on line 35, I've just returned unit from its function just to make it syntactically complete, give it something to do. So I'm going to go ahead, select that; but before I do that, I'm going to turn on timing in F Sharp Interactive because that will tell us how long the function has taken to run, how long it's taken to allocate these 10 million tuples, and it'll also tell us a little bit about what garbage collection is going on.
So let's go ahead now, send that to interactive and call it. And here you can see that took 1.278 seconds. Not bad for 10 million items, but it still might be significant if you're in a low latency situation. Let's see how that plays out when we use struct tuples identical code, except here I'm forcing these tuples to be allocated as value types.
We've still got timing turned on. Bang, 93 milliseconds. Let's just prove that wasn't a fluke by repeating. So that is our reference type tuples, one-and-a-half seconds. Our struct tuples, value type tuples, 46 milliseconds. So in some scenarios, and it's up to you to determine what those scenarios are, this can be an awful lot faster.
Kit Eason discusses the new value types that provide an opportunity for performance gains, the new result type which gives you access to the railway oriented programming style of error handling, and program organization and readability changes. Plus, he explores the evolution of tooling for F#, and explains how F# tooling has changed in Visual Studio 2017. To wrap up the course, he shares how you can contribute to the F# language and tooling by getting involved in the open-source community.
- Working with struct tuples
- Marking a record type as a struct value
- Marking a discriminated union as a struct type
- Using the fixed keyword to mark a value
- F# result type and associated functions
- Resolving potential naming clashes between modules and types
- Error message improvements
- The past and future of visual F# tooling in Visual Studio
- Reviewing F# tooling changes