 On today's Visual Studio Toolbox, Philip Carter will get us up to speed on F-Sharp 5, which is now in preview. Hi, welcome to Visual Studio Toolbox. I'm your host, Robert Green, and joining me today is Philip Carter. Hey, Philip, how are you? Hey, I'm doing okay. I'm just stuck in the house most of the time. Aren't we all? At least for a little while longer anyway. Welcome back to the show. I think it's been a while since you were on. Today, we're going to talk about the latest iteration in F-Sharp, which is coming soon, F-Sharp 5. Yeah, definitely. I think the last time that I was on was either in the lead-up to or just after the release of Visual Studio 2017 and F-Sharp 4.1, which was quite a while ago. Okay. 4.1, so we haven't skipped any versions in between. Actually, we shipped F-Sharp 4.5 after that because there was this weird versioning thing with some binaries and we just wanted to get all the numbers aligned and then we shipped an F-Sharp 6 along with Visual Studio 2019, the very first version of Visual Studio 2019. Then we shipped an F-Sharp 4.7 last fall. There's actually been quite a bit of content there. How does that work? How do you go from 4.5 to 6.4, 7.5 to 5.5? Well, so language versioning is just arbitrary really. The way that we like to think about it is, we were actually going to go with an F-Sharp 4.2, but the problem is there was this weird versioning thing with this core library called f-sharp.core, where it used to have this leading number called 4, but that didn't actually represent the language version, and that represented the CLR version because it was also a CLR version 3 and there were these two different binaries for different CLRs because they were not compatible with each other. But then we standardized on CLR version 4 for .NET Framework and then we said, okay, well, if we're not going to be shipping a CLR version 3 binary anymore, we should just adjust this language version, but the problem, sorry, the core library version, so the problem is the core library was version 4.4.1, so the second number was already a four, so we couldn't bump that down to two because that would break things. So we said, we're just going to move everything up to 4.5, even though it was just a minor release. And then 4.6 was a minor release and 4.7 was a minor release, but then F-Sharp 5, we're doing a major release because there's a lot more features that are sort of planned and we already have more features available on the first preview than I think the sum total of the features that were in 4.6 and 4.7. So by the budget, maybe F-Sharp 5 fully shipped later this year, we're thinking November with the .NET 5 release. It should have quite a few features in it. So we said, screw it, we'll just do the major version bump. There's a lot of features in there that kind of make sense to do it. So we'll just add it, yep. Yeah, so that's the other thing that I just kind of authentically mentioned. F-Sharp 5 is in preview right now. You can install the latest preview with the latest on a Core SDK or just with the latest Visual Studio preview, you'll get the same compiler either way. What I'm showing today is using Visual Studio. And we are intending on shipping F-Sharp 5, the complete version in November and the things that I'm going to show today are what's already available. In future previews, we're probably going to have more features that you can play with. So that's something that people will just want to keep appraised of as they look at release notes and just kind of follow along our development and that sort of stuff. And we'll blog about it in the .NET blog whenever we have any features to brag about. So briefly, before we start to look at it, give us just a high level quick overview of what are some of the broad themes you've been focusing on the last year and what the broad themes are for 5 and moving forward and do all that in about a minute. Yeah, absolutely. So the major themes for the past year, excluding F-Sharp 5, just kind of standard Core F-Sharp, we've shipped a ton of bug fixes and improvements just across the board for everything. And those bug fixes have been everywhere from the compiler to the tooling to the court library to just the F-Sharp interactive process to just sort of everything. We wanted to have a very stable base for everything because Visual Studio 2017 and F-Sharp 4.1 kind of introduced a whole bunch of features from the tooling side. But with that came, you couldn't turn some of these off. If you wanted to, sometimes they wouldn't quite work right and things like that. And so over time, they've been incrementally getting better and more configurable and that sort of stuff. And then the other thing has been tooling performance and reliability as a whole. So one of the things that I want to show is something that it's not necessarily the most exciting demo in the world, but it should be exciting for people who have very large code bases because doing this kind of operation, there's some operations that would either just fail or they would make Visual Studio crash because it would use up so much memory that it would just fail and we've sort of gotten it to the point where it doesn't do that anymore and also made it significantly more responsive which we're pretty happy about. For F-Sharp 5, the focus has just been improving the core language and then trying to get better interactivity with F-Sharp Interactive and Jupyter Notebooks in place so that we can... One of the things that we're interested in trying out is trying to find those Python developers out there who like static typing, but they don't like typing a lot of code. So there's a lot of Python developers out there who they love the succinctness of Python, but they want to have types. They want to have some structure basically to their coding, but when they get into C-Sharp, they go, oh, well, there's all these curly braces that it's not really for me. Well, F-Sharp also has a similar sort of way that it looks in terms of syntax and so we're kind of interested in seeing what some of those Python developers have to say about F-Sharp especially with F-Sharp 5 and some of the stuff that we're going to try to do to make interactive and kind of analytical programming better. So that's the whole thing there. Very good. All right. Let's see a demo. Yeah, absolutely. So I think what I'll do is I'll share my screen here as if this is just like a normal conference call except this is not normal because I get to show some fun stuff. So here I have Visual Studio 2019. I actually have the latest preview installed. And so what I'm going to do is I'm going to go ahead and open up the F-Sharp code base itself, which is really big. So if the total line of code in this, well, so first of all, the first thing that you should probably notice there is that everything is already loaded kind of without, like before you knew it, everything was already available. In Visual Studio 2017, I would still be stalling for time as the progress bar would say, loading solution, loading solution. Here's another thing. We have some fancy looking colors here. These colors wouldn't appear for about a minute in Visual Studio 2017. And that's not because the tooling is inherently bad, it's because this is actually an enormous amount of data that is being crunched by the compiler here. The F-Sharp compiler code base is about 300,000 lines of F-Sharp, which is about the, I think it's the largest F-Sharp code base in the world, to my knowledge, or one of the largest. And translating that into C-Sharp terms is about two to three million lines of code. So if you're familiar with that sort of framing, that's about the size of stuff that we're dealing with here. So here I have this thing called the unit type, an F-Sharp that's used absolutely everywhere in the compiler code base. So I'm going to hit find all references on this. So one thing that you'll notice is on the right hand side, I'm getting a lot of information already. It's finding a bunch of references. If I were to do this in Visual Studio 2017, it just wouldn't find anything actually. There was a series of bugs that just prevented it from being able to crawl like certain stuff within this code base. If you're using Visual Studio 2019 but you're not on the latest preview, right about now, things would probably start to come to a crawl and perhaps even crash. And the reason why is because, as you'll see here in a moment, the unit type is used many thousands of places in our code base. So there's an enormous amount of references going on. But then more importantly, we use multi-targeting inside of our code base. So the way that the Visual Studio tooling works is it actually computes everything twice if you do multi-targeting for two different target frameworks, because you might have code doing stuff differently for Donut Framework versus Donut Core or Donut Standard versus Donut Core based on the APIs that you have available. And so the compiler has to crawl sort of both contexts and understand where those references live if there's any differences. So another big difference with what's going on here compared to the, in fact, even Visual Studio 16.5, which is the latest release version, is you see this kind of loading bar at the top that's kind of scrolling around? That's basically representing, hey, we're crawling through the whole code base, we're building everything up, and then we're going to display some results. And Visual Studio 16.5, it crawls everything first and then it starts displaying results. What we do right now is when we have results, sort of that we've computed a unit of work and we have references that we're able to display, we just display them. So if I was interested in this particular project or this particular project or something like that, I could start looking for it. So this is a rather extreme case that I'm showing here, but I think it's probably pretty important, especially for those F sharp users who work in very, very large code bases. In extreme case, the unit type can actually be done. So it's kind of hard to see the number here, but for the .NET Framework 472 target, it found almost 1600 references. And then for the net standard one, it found almost 1600 references as well. So we're already past 3000 references at that point and this is crawling our entire solution. So this is kind of the big, I think sort of the big overall thing. And then I'm doing this right here and I can scroll, which is kind of important because in the past you weren't necessarily able to do that. Yeah, there's a lot of performance improvements kind of under the hood to represent this. And then the other thing is, once it sort of computed a bunch of stuff, it's a lot faster to compute things again. So like in this case right here, it was able to find already, you know, 3000 references to this thing because it had already crawled a bunch of stuff. Again, in Visual Studio 2017, this wouldn't do anything. In Visual Studio 2019, it wouldn't really do a whole lot. So yeah, we're pretty happy about that. But this is all just, yeah, it finished. So that's 6,200 references. That's not a very small amount. So this is all fun and dandy. This is, I think, you know, core F-sharp users who are just doing their everyday work should just notice that things work faster. If they do things like find references or rename, it should be more responsive. It shouldn't take less memory. It should just be faster as a whole. So that's kind of a big one there. And just to prove it, in 2019, I hit find refs, no references found. This is 17 here? Yeah, Visual Studio 2017. So we had 6,000 references going on in 2019 and exactly zero in 2017. That should be the big difference that you see there. So let's go ahead and close out a few things. So we talked about F-sharp 5. I want to show a little bit of F-sharp 5. Okay. Because even though F-sharp 5 isn't released, you can actually access it from Visual Studio today, especially if you're on BS 2019 preview. So the way that this works is we ship a compiler that is available across all channels, like Donacore Release, Donacore Preview, Visual Studio Release, Visual Studio Preview. It's the same compiler. But what we do is we have a little mechanism inside the compiler that lets you toggle between preview mode and regular mode. And so you have to opt into preview to be able to access these features. But you don't need to install any additional bits. You just install the compiler and then you say, hey, I want access to preview features and then I can do that. So the way that you do that in Visual Studio, the way that I'm going to show it today, well, there's really two different ways. The first one is you go into a project file and then you set your lane version. This is very similar to the way that C-sharp works. So if you've ever done that, then you can just do that in F-sharp. But the second one, the font's a little kind of small here because of the setting size. But if you go to the tools options and you go to F-sharp tools and F-sharp interactive, there's a new option there called FSI Preview, Enable Preview Language Features. And this is for F-sharp interactive. So if you're doing F-sharp scripting and you're using F-sharp interactive bundles inside of Visual Studio, you can turn it into preview mode so that you can start coding up in preview. And do you need Visual Studio Preview to do that? Currently, yes. When the next, when Visual Studio 16... No, actually you shouldn't now. Visual Studio 16.5 should have that option turned on. If you were on 16.4, then you wouldn't have it. Got it. Yeah. And then it's going to be there forever now. So this is kind of like a new part of tooling where if you want to access F-sharp interactive in preview, then you can do that. Okay. So the first preview feature that I'll briefly go over is Name of. All right. So I'll create a value and I'll give it your name. And then I'll go Name of Roberts. All right. Name of is a very underused feature. It's great to see it here. It's super, super useful. Yeah. So I'm just resetting my F-sharp interactive session there because I had some stuff going on there. So as you can see right there, it got Robert. So I could also type it in here. And, you know, all right. It was able to work. It got the name of the value right there. Yep. Another fun little quirk is you can actually do Name of Name of, which is useless, but it's just fun. You can't do this in C-sharp, so I just wanted to point it out. If you try to arbitrarily nest it, though, it's not going to work. This is, you know, I mean, you could, you know, do something like this. We should add that to user voice and see if anybody agrees. Let's see here. Okay. So, you notice that there's a meeting going on. Do you need to stop the video and join that other meeting? Oh, jeez. Let me close that there. I told them that I wasn't going to be joining. So you'll notice that I just kind of nonchalantly typed something. I said open system.math. Well, the thing is system.math is not a namespace. That's actually a static class. So we added the ability to open static classes in F-sharp. So it's, you know, I have access to Pi unqualified. I don't have to go math.pi. I can just go, whoops, Pi. And it's able to execute it and give me that. I can also define my own static classes. We'll just call it my static class. Okay. We'll go static member. Hello. And we'll go it's static classes are sealed and abstract. Right. So static classes aren't actually a real thing. Or rather, it's a facade. A static class is just a sealed and abstract class. This is the way you define it in F-sharp. And I could just type hello. So I can send all of that over to F-sharp interactive. And that'll say the strain hello. So in this case, you know, I can do that. And I can do this. We'll go open system.math. We'll go strain.pi. And I'll just send that over to F-sharp interactive. And it said hello, 3.14 blah, blah, blah, blah. So here, you know, I'm combining my own, you know, static class that I defined along with one from the framework. So this is going to be helpful for working with libraries that, you know, like, you know, that are basically libraries that are defined in C-sharp that expose DSLs via, you know, members in their static classes. And so this is a pretty powerful thing that we have here. But so some other interesting stuff. So, you know, I can, this is kind of getting more into the, you know, going after Python, you know, disinfected in Python people sort of stuff. So you can already do something like this today where you can say, hey, I want to create a list from one to 10. This is always been available in F-sharp. And, you know, and I can, I can filter it. I can do, you know, you know, from index 3 all the way to the ends, you know, I can send that over to F-sharp interactive. That'll, you know, okay, it's, you know, four, five, six, seven, ten, that sort of stuff. This is all normal F-sharp. But one thing that you weren't able to do was go from the beginning all the way to three to the end. Now you can actually do that. You'll notice there's kind of this difference right here, four, five, six, seven, eight, nine, ten, one, two, three, four, five, six, seven. And you'll notice that, you know, you know, the last one is ten, three from ten is just seven. So, you know, there's no like weird off by one stuff that you got to memorize, you know, that sort of stuff. It's just, it's pretty straightforward there. You can also define these on your own types. Right now, I'm not going to try to extend the span type right now because I think that has some, yeah, that says F-sharp interactives are on court. But so you can define these on your own types as well. So this is actually just done by a lookup of a particular member called the Get Reverse Index. And so what, probably in the show description notes, I'll probably have like a link to documentation that shows how to do that because I have like a full sample that works on a core there. I just didn't want to try to code it up live because when you try to talk and think and code at the same time, you can't do all three. So this is reverse indexing. And so, you know, you can also, you know, you can apply that to slices. So this is very similar. Like, I think this is important, especially for people coming from Python, because you can use sort of this very succinct range syntax to generate things, but then you can also use sort of the same syntax to slice things up. And you can take things, you know, from the beginning from the end, you can, you know, so basically if somebody's coming from Python and they're used to doing this sort of stuff, they can get it with F-sharp. And they don't have to really do anything special. Like, you know, I didn't have to declare any types or anything like that. And that's because, you know, this uses type inference to figure out everything rather than be dynamically typed, which is a big deal. So I showed name of static classes. I showed some slicing. There's a few, like, other things that we did in slicing and indexing that isn't really, like, that exciting from a demo perspective. It just kind of cleans up the behavior that used to be there. So if you've been using this stuff in the past with F-sharp, you should notice that things just kind of seem to work better. Like, there's just less edge cases, less stuff you got to account for, that sort of stuff. One thing that I want to show. Be prepared. There's going to be a big blast of code here. All right. 103 lines of code that I just pasted in here. So let me just walk through this very briefly. Okay. This is more for advanced F-sharp users, I'll say, than anyone else. But in, well, not really advanced. I guess I'll say more library authors. So we have this feature called computation expressions. Right. And what that really is, is you see, you know, this fancy stuff going on right here. Right. You know, this let bang, this thing called results. This thing called result, this let bang, this return, that sort of stuff. This is a way to sort of express when you're doing things in a particular context. So one example is an asynchronous context. Say I'm computing some stuff, but I'm doing it asynchronously. F-sharp has a way to represent that within its syntax that ties it in with the F-sharp type system so that it's very, very hard, basically, to do something wrong. Number one. And then number two, it lets you express things very succinctly. So like, you know, notice how I don't have, in this case, I have this thing called a result. Well, an F-sharp result is actually just a type that's either a value or an error. And the value, you know, it could be anything and the error could be anything you want it to be. It could be like a string. It could be an exception. It could be some custom data, that sort of stuff. So one thing that some libraries will actually do is they'll say, it sort of lets you do like this result-oriented programming. Like, you know, if you're working with, you're computing a bunch of stuff and you want to get some data out of it, well, it could return an error for some reason. How do you, there's sort of this question of how do you make consumers sort of account for the fact that something could be a value or something could be an error? One way to do that is to force them to use pattern matching, what we have here everywhere. And so that's fine for like just a single result because you can just, you just have the value, you just pattern match on it. That's pretty natural. But what if you have these successive things, right? What if you need a result and then based off the information from that result, you're going to calculate another one. And then based off the information from that, you're going to calculate another one and so on and so forth. Well, then you start having to nest pattern matches. You'd have to like stick one inside of this OK case because we got a value. Let's try to get another value. And then you could end up with like this pyramid of doom of just a whole bunch of match expressions kind of going out diagonally there. And that's kind of hard to read. So a way to flatten that out is what are called computation expressions, right? Basically this let bang binds to this value A when it's successful and when it's not, it doesn't bind to it. So this is something you've been able to do before. However, there's one catch to this prior to F sharp 5, right? Let's say I changed my code to be this. I've let bang A, let bang B, and let bang C. These are three individual values computed from three different results. If you do what's called monadic, it's the fancy monad term, but this is really sort of the form that it is. If you just do let bang A, let bang B, let bang C, let's say that A actually returned an error. This would short circuit out and go straight to the very end of the computation expression. But what if I don't want that to happen? What if I want to combine all results, whether or not they're an okay or an error? And this is sort of generalized out. This isn't specific to results. Like say I have some asynchronous work that I'm doing. If the asynchronous work fails for some reason or however I define failing, then it's just going to short circuit out of my routine. But what if I wanted everything to sort of compute and then just collect all the results at the end and do something with it based off of what I got? There was no way to actually express that prior to F sharp 5 today. Now we actually let you do that. So instead of doing this successive let bang, we sort of create this block called let bang and bang and bang. And so I defined a bunch of stuff up here that sort of lights this up. This is like a mini library that I've built here. And the key thing is I've added these two methods called merge sources and bind return. Again, this is for library authors. This isn't necessarily something that normal, like if you're not doing libraries, you're not going to really have to worry about this sort of stuff. But if you're writing libraries, this is helpful because this allows you to be a bit more expressive in what you want your consumers to do. So what I have here is I'm actually combining two forms what I'm calling monadic and applicative. And so what's going on here is it's sort of, you can almost think of it in parallel, computing R1, R2 and R3 and sort of saying, hey, I don't care what the value of this is yet. I'm just going to let these things run their course and then I'm going to try to combine their results. So what happens is if there's an error, it short circuits out at the end of the return rather than the first time it actually found an error. And so to sort of prove that this should work here, I'm just going to execute this in interactive. So I sent this whole library over here. So I have this function called run that takes in four different results. I produce a final value and then I pattern match off of whether I actually got something out of it or whether I got an error. And so the code that does all of that is actually two nested results. One is what's called this applicative form that lets you do things all kind of in parallel or independently really is sort of the key term. And then this one right here is a monadic form. So where, you know, if this one were to fail, then it wouldn't, you know, it wouldn't, you know, compute any of this. It wouldn't even reach here. It would just sort of short circuit out here. So let's call print applicatives. So this should turn out a few things. Okay. So got four and failure. So what's going on here is in the print applicatives function. Let's see. I can just go to definition there. I've defined four different result values. I've got an R1, R2, R3, and R4. And then I'm calling the run function once with all of those. So these are all, you know, actual values. So I should expect a value out of this. But then in the second one, I threw an error into the mix. So I should see the error because like you can't combine an error with other things. And so that's why you're able to see, you know, how you can get a lot of work and failure here. So this is a much more involved feature. This is kind of much more advanced F-sharp programming. But, you know, the sort of key takeaway is that if you're a consumer of libraries in F-sharp, you should find that your libraries that use computation expressions are going to be more expressive and allow you to do things like compute independent things all at the same time without short circuiting out. And then if you're a library author, you basically have more toys to play with now. You can extend your libraries to be more expressive. You can do, you know, basically just dance your stuff. And a lot of library authors like having more toys. So we like to make them. Cool. Yeah. So this is really just sort of the key set of things that are in F-sharp 5 right now. As I mentioned, there's a few more things. They're just kind of more oriented around cleaning up existing experiences, making things feel a bit more consistent. And so really this whole package right here is, like, much better performance in Visual Studio and just F-sharp tooling as a whole. Much more correct behavior. Like, you know, you saw in that Find References, it just didn't even do anything with Visual Studio 2017. With this one, it actually does. It finds many thousands of references. And then you can toggle your preview stuff so that you are accessing a bunch of interesting features that still haven't shipped yet. So that's really sort of the big set of different things. You know, there's actually, there's also more. There's F-sharp 4.5, F-sharp 4.6, F-sharp 4.7, those features. But I'm not going to spend two hours trying to go over all that. I'm not going to give it to you either. All right. Well, that's an excellent overview of what's coming in F-sharp 5. And again, it's in preview now. We'll have links to that in the show notes. Thanks so much for coming on and showing this to us. Yeah, absolutely. Happy to do it. It's great to see you again. Yeah, you too. All right. Hope you guys enjoyed that. And we will see you next time on Visual Studio Toolbox.