Venue: Skype
Eberhard Wolff talks with Phillip Carter about F#. A multi-paradigm programming language that supports object-oriented, imperative, and functional programming, F# can be used for a broad variety of applications. It’s an especially good fit for parallel programming and DSLs. Type interference allows F# code to be type safe even if no types are declared in the code. The F# Software Foundation runs the language’s development. Microsoft supports F# in Visual Studio and on the .NET platform, so it’s a good example of Microsoft’s approach to open source.
Show Notes
Related Links:
- Phillip Carter on Twitter https://twitter.com/_cartermp
- Introduction to F# https://fsharpforfunandprofit.com/
- Wikibook about F# https://en.wikibooks.org/wiki/F_Sharp_Programming
- Try F# http://www.tryfsharp.org/
- F# at Microsoft Research http://research.microsoft.com/en-us/projects/fsharp/
- F# Software Foundation http://fsharp.org/
- Visual F# https://msdn.microsoft.com/en-us/library/dd233154.aspx
Transcript
Transcript brought to you by innoQ
This is Software Engineering Radio, the podcast for professional developers, on the web at SE-Radio.net. SE-Radio brings you relevant and detailed discussions of software engineering topics at least once a month. SE-Radio is brought to you by IEEE Software Magazine, online at computer.org/software.
* * *
Eberhard Wolff: [00:01:16.08] Welcome. This is Eberhard Wolff. I’m sitting here on Skype with Phillip Carter, who is the program manager for F# at Microsoft. Thank you for finding the time, Phillip. Do you want to say a few words about yourself? Introduce yourself, what you are doing and how you are related to F#.
Phillip Carter: [00:01:33.07] Hi, thanks for having me on the show. I’m a program manager for F# at Microsoft. I work on the visual F# language and tooling team. We work on the compiler, we work on the tooling in Visual Studio and we work on integration with .NET Framework, .NET Core, and all things .NET.
Previously, I had worked on the .NET Core team. One of the things I focus on now is getting F# to run smoothly on .NET Core so that we can have official cross-platform support for F# on .NET.
Eberhard Wolff: [00:02:10.00] Can you say a few words about what F# is?
Phillip Carter: [00:02:15.06] F# is a functional-first multi-paradigm programming language. It’s a lot of words, but basically it’s a general purpose programming language that you can effectively do anything you really want to with. However, we put the skew on doing things in a functional way, adhering to functional programming concepts, such as immutability, higher order functions, less manipulation, data wrangling and so on.
Eberhard Wolff: [00:02:43.04] Every language is somewhat influenced by other languages, and there is a whole tree of history of languages. Which programming languages was F# influenced by?
Phillip Carter: [00:02:52.14] F# is influenced by a number of programming languages. The most core language that it’s probably influenced from would be Standard ML, which is this research programming language that is oriented around the Hindley-Milner type system. There have been multiple derivatives of that, such as OCaml and Haskell. They’re all in this family of languages, and F# is another one of these derivative ML languages that’s a peer language with Haskell and OCaml.
[00:03:23.18] There are pieces of Haskell and OCaml that have made their way into F# as an influential thing; some bits from other languages have made it in, as well. On the other side, F# has influenced a lot of what’s going on with C# and Visual Basic, with some of its features that it pioneered early on.
Eberhard Wolff: [00:03:44.21] When you say it has been influenced by the ML and Haskell language family, is it the features or rather the syntax that it took from those languages?
Phillip Carter: [00:03:54.28] It’s both. The syntax is definitely the more obvious one. Not necessarily from Haskell, but if you were to look at Ocaml code, for example, it looks really similar to F#. In fact, they’re the ones that are the most similar. They share a lot of keywords, and you could write in OCaml, F# and it’s generally speaking not too difficult to port it to one or the other. There are some quirks with the language that you have to work out, but there are some of the features, like having a more powerful type system, having type inference in the language is something that F# was certainly influenced by from those other languages, in addition to ML.
[00:04:34.14] That’s one of the core things of that family of languages, the type system. What I’ve mentioned is the Hindley-Milner type system, which is this way of inferring types from things, and it’s this algorithm that was derived by these computer scientists. I believe it was the late ’70s or the early ’80s where they effectively came up with an efficient way to understand the types of things at compile time, so you wouldn’t have to explicitly annotate every single type if you wanted to use it as that particular type. That’s a central thing that influenced F#.
Eberhard Wolff: [00:05:09.04] You said that languages like C# are influenced by F#, so what are some features that C# took from F#.
Phillip Carter: [00:05:17.15] I want to be careful when I say ‘took’, because it’s more of an influential thing. With F#, an example is language-level asynchronous programming, also known as async workflows in F#. These were introduced before language-level async made it into C#. Although they have different concepts for how asynchronous code is handled, F# was a pioneer in that it was, “Hey, can we make asynchronous programming a language construct and have syntax around that, and will people like it?” The answer was yes, people liked it a lot, so C# was able to say, “Okay, people really do like this thing, so what’s a way that we can have language-level asynchronous code in a way that feels natural to C#?” That’s the type of influence that we had there.
[00:06:04.01] Some recent things that are coming along now with C# 7, most notably tuples now, but then we’re going to have pattern-matching coming along pretty soon – these are things that are also very foundational pieces of F# that people love using. C# said, “Okay, there is a particular style of programming, often times working with data, that people love these language constructs for.” They can look at it and say, “Okay, this is how they do match expressions, this is how tuples are deconstructed, this is how tuples are returned from functions”, and we’re seeing that same sort of influence happening right now in things that are being actively developed with C#, and also with Visual Basic; t also has the asynchronous programming.
[00:06:52.22] There’s a prototype for Visual Basic right now that with the preview version of Visual Studio you can use pattern matching. Those are the areas where it’s been influential right now.
Eberhard Wolff: [00:07:05.06] Does F# only work on the .NET platform, or can it also create native code? Are there any other platforms that are supported?
Phillip Carter: [00:07:11.20] Right now it is only .NET. When I say “only .NET”, that means a couple different things. First of all, when you talk about concrete implementations, it means the .NET Framework, which is the runtime and garbage collector that have existed in .NET for a very long time; we have .NET Core, which is a cross-platform runtime and garbage collector that works on lots of different Linux distributions and Mac, and it also runs on that, although in an alpha state right now. We’re working towards a 1.0 for F# on .NET Core.
[00:07:42.19] It also runs on Mono, which is another implementation of .NET that’s entirely cross-platform. There’s these three core places that it will run. However, outside of those, there isn’t any support. There is no support that I know of for native F#. We do have a .NET native compilation toolchain that we are looking to support F# with; however, because that’s still very early technology, there are quite a few areas where F# will just simply not work on that right now.
[00:08:14.21] We anticipate being able to generate native code from F# with the .NET native toolchain within the next year or two.
Eberhard Wolff: [00:08:23.14] Are you planning to do an implementation on the JVM?
Phillip Carter: [00:08:25.10] No. At least certainly not that I know of.
Eberhard Wolff: [00:08:28.14] There are quite a few languages – you mentioned OCaml yourself, and there is Scala in the Java world, and that’s quite huge, there is Haskell, and that’s also gaining some traction… Why not just port one of those languages?
Phillip Carter: [00:08:42.16] That’s a pretty good question, because those are awesome languages. Scala has amazing features; personally, I’m a huge fan of [unintelligible 00:08:50.14] in Scala and type classes in Haskell; it would be awesome to have that work in F#. Actually, Haskell, for a time, did have .NET implementation, I just don’t think it was actively maintained.
Because I wasn’t involved in the creation of the language, I can’t specifically say why not just port one of them, but to be frank, it’s just really cool creating programming languages. You have that level of control of saying, “Well, there are these little quirks with this one language that I don’t really like. Is there a way that we can flip those around, or do what we think makes sense in this context?” I would imagine it would be one of the motivations there.
Eberhard Wolff: [00:09:31.21] So it’s not about creating functional programming languages for the .NET platform and using the specifics of that platform – that’s not the reason why F# was created…?
Phillip Carter: [00:09:43.02] We did absolutely want to have a functional programming language on the .NET platform. But taking an existing functional programming language – that was not one of the reasons that it was taken for… I guess it’s just speculation on my end, because I wasn’t involved in creating the language. There are some tradeoffs that are made in certain languages that may not necessarily have been appropriate for .NET; at least not at the time that F# was created.
Eberhard Wolff: [00:10:10.08] You already spoke about type inference, and that’s quite a great feature. Can you say a few words about that? As I understand it, you don’t have to declare any types anymore.
Phillip Carter: [00:10:24.07] Yes, in a way. As you said, you don’t have to declare types. The compiler is able to figure out what things are. There are some limitations to that, usually when you’re attempting to use a variable. Say you have a parameter to a function and you did not annotate the type; that is an option – you can annotate the types, but say you didn’t, and you used it in some way that might be ambiguous. The particular operation that you’re performing on it might be valid for more than just one type. At that point, the compiler will say, “Hey, I need you to tell me specifically which one this is, because out of this vast array of functionality that could be performed on a type, there happens to be some ambiguity going on.”
[00:11:08.20] That’s usually where the limitations are. That’s a very few and far between thing. If you were to write a large solution in F#, well over a thousand lines of code, very little of that would ever include annotated types. That buys you a lot of things, because first of all, it’s less code. Less code means less bugs, because there’s less stuff that could possibly go wrong. The second thing it buys you is less cognitive overload; reading code is always one of those things that takes a lot longer than writing it. Well, it often times takes a lot longer.
[00:11:47.01] If you’re reading a large file and you’ve never seen it before, things like type annotations everywhere get in the way of you trying to understand what the code is actually trying to do. It’s a huge benefit when you’re looking at a codebase that you may not fully understand yet.
Another awesome benefit that type inference has is you can write a function, take some parameters, you do some operations on it and then you return a value. You may have thought in your head as you were writing it, “These are operations that are happening on a very specific type”, and you just chose not to annotate it because it was less stuff for you to type into the computer, but the compiler may have actually said, “Oh, but what you’ve done here is actually a little bit more generic.”
[00:12:33.06] You can sometimes inadvertently write more generic code, because the compiler says, “This routine that you’ve written here is valid for more than just one type”, so you can then reuse that in more places. That code reuse thing is one of those — I like to call it ambient code reuse, because as you’re writing the code you’ll notice that you can use your functions in more places than you may have thought you could have. Because the compiler infers the types for you, you’re able to reap that benefit.
Eberhard Wolff: [00:13:04.09] Do you have any idea why there are not a lot more languages that use this concept?
Phillip Carter: [00:13:10.28] One of the answers is that it’s really hard. The type inference algorithm that we use is based off of something that’s very established, but it’s still an exceptionally challenging thing to get right. Not just from a correctness standpoint, but from an efficiency standpoint, because this is all at compile time [unintelligible 00:13:27.01] happening at runtime. If you don’t have a good implementation, then your compile times just explode.
[00:13:35.26] A great example of this is something that recently came up with Swift. Swift is an amazing programming language that Apple has come up with, that I really love using and I encourage everybody who’s a fan of programming languages to go out there and try it out, because it’s really great. But they had a bug where someone was writing a unit test; he had been using type inference and he just declared a dictionary that was hardcoded for a unit test, so that he could compare it with the result of whatever he was testing. And it took 12 hours to compile, because it happened to go down a path where the Swift compiler ended up in this really inefficient pathway of trying to infer what type this hardcoded dictionary actually was.
[00:14:20.06] Things like that are really challenging. I think it’s less common, because it’s such a difficult problem to tackle that many languages choose not to tackle it. At least that’s my perception.
Eberhard Wolff: [00:14:33.22] It’s interesting that you said that it’s easier to read code that users type inference. I would think that if you annotate a parameter or a variable with some [unintelligible 00:14:43.16] sort of a documentation, because what you’re saying is, “This should really be this type.” What do you think about that? Do you think that’s a valid argument, or would you say that generally speaking type interference is superior in that regard?
Phillip Carter: [00:15:01.18] I definitely view it on a spectrum. In the large, when you have one big file or a lot of files, tons of things going on there, if every single thing has an annotated type, my experience with that sort of stuff is a lot of that ends up being noise. If I were to call some library function that got a string from an HTTP get, then I would know that it’s a string because that’s what the data is. The fact that the result is annotated with a type string isn’t necessarily going to be relevant information for me if I just want to find out what’s going on there.
[00:15:41.04] However, sometimes it definitely is a good way to document exactly what this thing should be doing. I view it as a case-by-case basis. If you think, “Hey, if someone’s reading this code and they may not necessarily understand that this valuable is supposed to be a string”, then annotating it with a string makes perfect sense to me.
If you’re declaring a class in F#, for example, and in the constructor you’re taking in something called “IP address and port”, IP address – a lot of people might use that as a string representation, but if the particular API that you were using to register the IP address didn’t take it as a string, it took it as something else, then annotating it as that something else is definitely helpful because it’s not a common way that people might represent that.
[00:16:35.19] I might even say that for common cases where you bind results to values, or you have parameters as functions, and if it’s a very common way that you would represent something, then type inference and not needing to annotate types is a benefit. But if it’s something where it’s not necessarily the most obvious thing, then type annotation is entirely possible for you to do, so you could go ahead and annotate it, and then it’s very obvious what needs to happen.
[00:17:04.00] There’s also a bit of an analog here. In F# there’s something called type declaration. There are these files that usually sit alongside your source files. Say you have your source.fs, which contains your code for doing something. You could also create a file called source.fsi, which is where all of your type declarations go.
You could say, “Okay, I have this module called Foo, and inside the Foo module I have these three functions.” In these three functions, all I have is a type signature where I say, “This is the name, this is the input in its type, this is another input in its type, this is the result in its type.” Then you could put any kind of documentation there, you can add labels to the types to give them names, and so on.
[00:17:49.15] That’s an effective way to document code from an API level perspective, because then you can point people the .fsi file and you can say, “If all you care about is consuming this particular function, then you don’t have to look through the potentially big source file that contains a whole bunch of logic. You might just want to know what the input/output types are.” In that case, a type declaration file like that is incredibly helpful.
F# has type inference, but you can declare types if you think that you need them, or if you have to use them. There are type declaration files that you can use to make things a bit more clear… There are a lot of tools in your toolbox. You’re not opted into just one specific way of doing things, and that’s something that sets F# apart from many other languages.
[00:18:44.08] You can choose what tools fits what you’re trying to do the most, instead of having to use one particular way. That having to use one particular way thing is something that is more common throughout most other languages.
Eberhard Wolff: [00:18:57.03] Do you have any idea how often those three features – type interference, declaring types in line or having these separated files – are used?
Phillip Carter: [00:19:13.11] I would say that having no type declarations is the most common thing. Most F# code will not have it, mainly because it doesn’t really need it. If it’s good F# code, usually it’s very obvious what’s happening, so it’s not really needed. In the cases where you need to disambiguate, or the cases where what you’re doing may not necessarily be obvious, that’s 1-2% of the time, depending on what’s going on. Then you’ll have those in-line type declarations, or annotating those types in-line.
[00:19:47.03] The type declaration files, those are very common for API developers. With F# Core, which are the core libraries that we have that have a whole bunch of functions on various types and extra types declared, that’s where all of our asynchronous programming is defined – for every single file we have a corresponding declaration file that has all the declared types for it. That’s where we put our API documentation.
In Visual Studio, if you hit F12 and then you go to definition, it actually takes you to that file and you can see, “This is the API code comments that we have in there”, and underneath those code comments you can see the type that the compiler looks at when you’re trying to use this particular function.
Eberhard Wolff: [00:20:37.08] We already spoke about async blocks, asynchronous programming and how that influenced C#; that seems to be another very important feature of F#. Can you say a few words about that and how that concept works?
Phillip Carter: [00:20:52.06] Asynchronous programming with F# is born out of a number of problems. First of all, asynchronous programming is a difficult thing. The context in which something is executing can be different than the context in which your normal code might be executing, and reasoning about that and thinking about what’s going on in this context versus that context, how do I communicate, how do I take multiple asynchronous contexts and have them talk to one another reliably – that’s a very difficult problem to solve.
[00:21:25.23] Traditionally, the way you would try to solve that would be with callbacks. There’s the infamous saying in Node.js with callback hell, where you have this triangle of doom of all these nested functions, and it’s not really clear what’s going to be executed and when. That sort of a problem has been around for quite a long time. F# is not the first language to have language-level asynchronous programming into it with these async blocks, but it is one of the first ones to adopt it in a way that’s very easy to understand.
[00:22:01.22] The way it does it is that it utilizes computation expressions in F#, which are these ways of being able to deal with types, and what we call ‘elevated types’. An elevated type might be an option of T, or if you’re familiar with the C# in Visual Basic world, the representation of a background task is a task of t (Task<T>) and the T inside of that is the thing that you care about, but the stuff that’s going on asynchronously, you have a handle to that with this task. That is an elevated type, compared to the T. You need some way to package that up into that task, you need to unpack it when it’s appropriate, and that can also be a challenge.
[00:22:45.01] Computation expressions are this general approach to being able to do that. We’ve taken that general approach and honed in on a very specific way. We say, “Okay, how can we represent stuff that is happening in an asynchronous context as an elevated type, and how can we use this computation expression syntax to make it really easy to work with?” They’re known as async blocks, where you have a function – in this case you would say, “let my function async is equal to”, and then you use this keyword called ‘async’ and there’s these brackets inside of it. Inside of that there’s a few special keywords.
[00:23:22.10] The way that you would bind the result of something to a name, in normal F# is you say “let x equal to” whatever the result is. But in this case if you say “let! x is equal to the result”, then that’s a way of saying, “In the context of this asynchronous block, assign the results of whatever asynchronous thing is happening to x, but only after it’s actually finished.
You can utilize that to call this asynchronous stuff inside of an async block really easily. The thing that you get when you call an async function that uses an async block is what’s known as an “async of T” (Async<T>). That’s a specification of a work to be done asynchronously, instead of work that’s already happening asynchronously.
[00:24:08.23] A way that I like to reason about it is, say I needed you to go do something for me. In a traditional world, I might tell you immediately what you need to do, and then you go off and do it, and then you come back and you give me whatever I need and then I’m happy. In the F# way of doing these things, instead of me immediately telling you what you need to go do, I would then write down a specification of what you need to do, and that specification might involve multiple different things that you need to go; maybe you need to go to this store, and then this store, do something with the things that you got from the store, all that kind of stuff.
[00:24:42.24] I would write that down as a specification, and then explicitly hand you the specification to perform that work, rather than telling you directly each thing that you have to do. That’s the model that we have for async blocks, where we can say, “When you call an asynchronous function that is defined by an async block, you get a specification of the work that happens in the background.” Then you can choose to start that work in a way that is most appropriate for you. Do you start it on the main thread and then await its result? Do you queue it up to run on the thread pool and then go do something else? Do you turn it into a different type, like a task of T (Task<T>) and then pass it into some C# code or some VB code?
[00:25:25.03] There’s a lot of flexibility there. Once you have that asynchronous workflow that you’ve defined, you can do with it what you will. Those are the pieces of what async blocks are.
Eberhard Wolff: [00:25:39.19] Is that specific to F#, or there are other languages that do similar things?
Phillip Carter: [00:25:47.13] I never actually used it in Haskell, but Haskell does have language-level async with some of their monadic do-notation, and it might also kind of do the same thing.
There are two models here: the hot task model or the cold task model. The hot task model is that by invoking an asynchronous function, there’s a side effect where something is started in the background. The cold task is saying invoking it gives you a state machine, and with it you can do what you will to then start that background task.
[00:26:17.28] I don’t know what model they use, but I wouldn’t be surprised if it was fairly similar to what we have.
Eberhard Wolff: [00:26:24.23] These async blocks – is that something you can use in ay other .NET language, or is it specific to F# only?
Phillip Carter: [00:26:33.14] In this case it is specific to F#. You cannot use async blocks in any other .NET language, and the model of asynchronous programming in C# and Visual Basic, while they do have async keywords for methods, it is a different model. I’d mentioned the hot task, which is called task model, where an F# invoking the function doesn’t start that background task; you have to actually explicitly start it.
In C# and Visual Basic, invoking the method has a side effect of also starting the background task. The two types, task of T, in the C# and Visual Basic world, and async of T in the F# world aren’t completely compatible with one another. There are some utility functions in F# in our core libraries where you can convert an async of T into a task of T, and you can convert a task of T into an async of T, but because they are these different models, you do need to have those conversion functions in F#.
Eberhard Wolff: [00:27:29.18] I see, so there is some compatibility layer there so that you can actually use the ideas in the other languages, too. Another feature that I found interesting is that F# has support for unit of measures, like meters, kilometers, grams and stuff like that. What’s the reason for introducing that and how is it useful?
Phillip Carter: [00:27:51.02] Units of measure are one of my personal favorite features of F#. There’s this problem with integers, floats and doubles, where the numeric value itself has a contextual meaning. The number 12 might be a meaningful number in some contexts, or it just might be the number 12 and a totally useless thing. Most programming languages will usually have some kind of API and some language that you have to use, where an integer is a value that you have to pass in, or a float is a value that you have to pass in, and often times it’s not really clear what the valid range is. Maybe it throws an exception if it’s not in a particular range.
[00:28:35.26] Say I was writing an API that took in the latitude and longitude and you passed in a latitude and longitude value that were not valid for the planet Earth. The function will probably fail in that case, but there may not have been a way for you to necessarily know that as a consumer of that API. Passing in 5,000 as the latitude for a point on Earth is totally valid as far as the compiler is concerned, because it’s an integer.
[00:28:59.12] Or say you’re doing something that’s time-sensitive, and seconds are measured from 0 to 60, or 0 to 59. If you passed in a value that was higher than 59, or a value that was lower than 0, in most languages that’s perfectly acceptable. In F# you can have more control over that. You can say, “This integer right here is a minute.” When you that, if you just tried to pass in the value 12 to a function it would fail, because it would say “You passed in an integer, but I don’t know what that integer actually was. That integer could be anything at all. You need to tell me whether it’s a minute, a second etc.”
[00:29:42.25] At compile time you can say, “This value is context-sensitive and I’m going to apply the proper context as I consume the API which uses these numeric values as inputs.”
That’s a powerful thing, because you could have a function say “Add minutes” that takes in the current time in minutes and then some minutes to add. In F# you could declare those types in-line or with a type declaration file as a unit of measure for minute, that you may have defined somewhere else. Consumers of that would have to pass in explicit integers that are minutes; if they were to pass in just regular integers, integers that were seconds or anything other than minutes, then the compiler would come back and say, “You didn’t pass the correct thing there.”
[00:30:26.20] That’s incredibly helpful for any kind of things where you have numeric information that is more than just a number, that actually has meaning.
Eberhard Wolff: [00:30:35.14] Can you also define that you may only pass an integer for a certain range, as you said with the longitude?
Phillip Carter: [00:30:43.26] There isn’t really support for this general purpose. Defining, in this case, a function which determines whether an input of a particular type is valid and embedding that into the unit of measure – that itself is not possible. What is possible is eliminating you from just passing in a number, period. Your number needs to be a specific type of number. That’s something that’s helpful, because if the number itself is of type minute, then you as the programmer can say, “Well, because this is a minute, it probably does have to fall into a particular range.”
Eberhard Wolff: [00:31:21.02] F# has some features around DSLs (domain-specific languages). How does that work? How would you create a DSL in F#?
Phillip Carter: [00:31:31.13] That’s a very common way to use F#. There’s not really this language-level specific for DSLs kind of feature in F#, however there are some really useful things for writing a DSL that are usually related to the type system.
I should back up for a minute. It’s not necessarily that a DSL is some sort of feature of F# that you can have, it’s that how you define a DSL is how you define your parser. F# has strengths for parsing things that will be very beneficial for creating the DSL. The core problem is that when you say, “I want to create a DSL for my language”, what you’re actually really saying is, “I want to define a parser for some input”, and that parser needs to be capable of handling all forms of that input, it needs to be able to handle it in a very correct way.
[00:32:29.04] There are a number of tools in your toolkit. Your first one would be FParsec, which is a third-party library which is very similar to the Parsec library in Haskell. It has a lot of built-in types that are beneficial for really common components of the DSL that you might use. It’s got some built-in parsing techniques so you could just pass in a type declaration and a string, and it’s able to validate it based on that.
[00:32:57.10] Outside of third-party libraries, the type system itself for F# is capable of doing a lot of things. For example, active patterns is a feature of F# which allows you to expand pattern matching in F#. As a [unintelligible 00:33:11.15] for pattern matching – if people are not familiar with pattern matching, you can imagine a superpower switch statement where you can say, “Here’s an input that’s of this type. In all these different cases, it could be this type, this other type; maybe it’s a tuple type, so maybe one of them is this type and one of them is this other type. Maybe one is empty and one is not.” There are all these different ways that something could be passed into something that’s valid, so pattern matching is a really nice way of having that in the language, and F# has that.
[00:33:50.11] Then you can extend that with active patterns, which are a way of associating a pattern with some functionality. A really basic way that you could do it is you could say, “Here’s a string. We want to check the case where the string contains a particular substring. Then you could define an active pattern that is able to take an input string and tell you whether or not it contains a substring, and then it creates an optional type which then you could use in pattern matching where it would automatically go to the case in your pattern matching expression of when it matched that particular string. Or it would immediately follow through to an error case that you would have to find, and it would just short-circuit down to the error case.
[00:34:31.28] This built-in language support for matching a case where it works and then short-circuit failure to the case where it’s supposed to fail is really nice, first of all because it’s a powerful construct, and second of all it’s not much code; you can define that parsing logic really concisely. It plays into the type system.
If you’re able to leverage the type system even further, then you could say, “This function which does this sort of advanced pattern matching thing ultimately returns this specific type”, and you can let type inference generalize that out if it needs to, and you can leverage that in other places, so you wouldn’t need to copy that code necessarily.
[00:35:14.03] The type system for F# itself just for defining record types and discriminated unions, which are known as algebraic data types, which are these recursive types where a type could also be composed of other types that [unintelligible 00:35:27.16].
Say you wanted to represent a binary search tree. Binary search trees are recursive types where you either have an empty tree or you have a node, and then that node could have two subtrees. Those subtrees themselves are defined in terms of your recursive type that you have them solve. Those two subtrees are also a case of either an empty tree or a node with two subtrees. And because the type system supports that, you can very concisely represent sometimes that more advanced type information in a way that the compiler also understands.
Eberhard Wolff: [00:36:04.28] That sounds pretty powerful. Given all these features and how powerful F# is, is it meant to be a general purpose language, or is it something that you envision to be used in specific use cases? What is the typical F# programmer trying to do with the language?
Phillip Carter: [00:36:23.01] I would say it is definitely a general purpose language. Things like object orientation are fully supported in F#, you can create classes, interfaces, abstract classes – you can organize your code in all those different ways. You can use F# for building user interfaces, for example Xamarin. You can build iOS and Android apps with Xamarin in F#. You can actually do it with Xamarin.Forms.
[00:36:46.09] It’s excellent for building web services, because often times you have some HTTP function, something that maybe sits on a controller – that can conceptually be modeled as a function. The input is some HTTP and the output is some HTTP, so that’s also a natural fit for F#; it is general purpose.
Should it be a language that you should do everything in? Not necessarily. The same applies to pretty much any language, including VB and C#. These are very different languages. VB and C# tend to be imperative and object-oriented first, with some functional features, where F# is functional first, with some imperative and object-oriented features.
[00:37:35.10] It depends on the problem that you’re trying to solve. If you have a system where a certain component of it really makes sense to be imperative, then you should write imperative code in a language that is easy for you to write it in, like Visual Basic or C Sharp. If you have problems that you need to solve that are functional in nature, where the vast majority of it is functionality, then F# is a fantastic choice. When I say functionality, that can mean anything – writing a DSL, writing web services; maybe you’re doing some mathematical thing, maybe you’re not. Maybe you’re just doing general text processing, maybe it’s just a rule engine for some business programming.
[00:38:18.09] You have a whole bunch of different rules that define how things need to talk to one another – that’s all functionality that can be written in F#. There’s not really a limit there. There are a lot of different people that use F# for different things.
For example, Jet.com is one of the primary competitors to Amazon. They have something like 700+ microservices written entirely in F#, posted on Azure, that define all of the rules for how things work with their inventory management, with displaying stuff on the website, with displaying what pages are, dynamic price changing, instrumentation of all of that stuff, checkout… Every single component of the website is defined in some way as a microservice written in F#.
[00:39:06.29] There’s another startup in the Silicon Valley area called [unintelligible 00:39:08.19] that does work in the energy industry. There are a lot of things like analytical dashboards needing to collect information from all these different devices, understand that information and organize it in a way that people can make sense out of. All of that is also written in F#.
There are also financial groups in New York City that write finance code in F#. It’s a wide variety of things that you can do, and that’s why we want to emphasize that it is a general purpose programming language that you can use for just about anything. If it makes sense for you to use it, then it’s a good idea to use it.
Eberhard Wolff: [00:39:48.27] Interesting. My impression so far was that the rise of functional programming and everyone’s interest in it is primarily due to the different way of doing parallel programming. Would you agree? From what you said, it seems like it’s a broad variety of things that are implemented using F#, so it’s not specific to just parallel algorithms, and things like that.
Phillip Carter: [00:40:16.03] Things like parallel programming, asynchrony and all that kind of stuff, there are some problems there that functional programming in general is very good at solving. Especially with the rise of cloud computing and a tendency to break up monolithic services into microservices, to be more concurrent, have things that are distributed across different machines (perhaps even different clusters of machines), you basically end up in a scenario where correctness is what matters.
[00:40:43.24] When you have information traveling from one machine to another, how fast something could be implemented on a particular CPU, if these values in this array are loaded in this particular cache line, in this particular cache level, and hit this register at this time – that low-level stuff, although it’s really important, becomes less of a priority when you’re building something like an insurance marketplace. In that insurance marketplace you’re going to have to collect all kinds of information from all over the world, save it in all these different places and have all these different backup strategies.
[00:41:17.13] When you’re dealing with information coming in and going into various places at different times – it’s not the synchronous thing; it might be happening in parallel, it might be just concurrent in general – then you care about things like immutability by default, and that things don’t change on you when you don’t expect them to change, and you care about functionality where there’s no side effects; it’s just input and output, and organizing things that way.
[00:41:42.01] People are more interested in functional programming in general because of that, because their problems have effectively risen to that level, where functional programming makes a lot of sense for them. F# is a way that you can do that on .NET, and there’s amazing tooling that you can use for that. You can use Visual Studio and you have a great IDE to support you in solving those problems with F#.
[00:42:08.25] There’s a bit of an impact that functional programming in general is having on the industry right now. I see it on the rise, and a lot of people do, as well. Interest is a lot higher, people are starting to rewrite things to be more functional, because that’s just where problems in general seem to be going.
Eberhard Wolff: [00:42:24.22] Let’s talk about a completely different subject for a minute. Who’s actually developing F#? Is it just Microsoft, or what kind of organization is behind that language?
Phillip Carter: [00:42:35.13] F# is in an interesting place. The concept of F# is not Microsoft-owned. It’s an F# organization that effectively owns F#. Microsoft owns an implementation of F# that we call Visual F#. This is a language and compiler and tooling that lives in Visual Studio and that we have with Microsoft tooling. So although F# kind of came out of Microsoft, it is a free and open source language that is very actively developed on by open source contributors.
[00:43:12.02] To list off some recent things that have happened, one of the community members, Steffen Forkman, has been working very heavily on changing error messages and making them a lot better.
We had another community member who implemented direct representation of the record type in F#. That’s a really big, challenging change to the language that not many people would normally do in an open source world.
Often times, it’s usually some primary people that work on an open source language or tooling, and then there are some contributors, but usually it’s minor stuff. We have community members doing really hard and important stuff at the language. They are also driving the design of the language.
[00:43:54.25] Our design process is done entirely on GitHub, entirely open source. We create an RFC and we say, “Hey, this is a proposal to change the language, to add a new feature, or to do whatever we think is appropriate for the language.” Then we open it up to the community and we say, “We value your input.” Sometimes those RFC’s are created by community members. Sometimes they’re almost entirely driven by community members, and by the time we at Microsoft even get around to thinking about how we might implement something, a community member has already made a pull request with their implementation. Then we can say, “Oh, awesome.” Now we can review this and talk about it for a few months and work it out, work on the little details. It’s a really fantastic way that it’s all organized.
[00:44:38.17] There’s an F# Software Foundation with a board of trustees that drive the organization itself; they organize talks and they help with visibility and outreach of the language. Microsoft does some of that itself.
At Microsoft, we work very heavily on the language itself, but we also spend significant time on the tooling and making sure that the language runs smoothly and all of the changes to the .NET platform that we create as well, so that as the .NET platform evolves, the F# language can also evolve on top of it, and never get left behind. There’s that sort of working relationship that we have going on.
Eberhard Wolff: [00:45:15.25] Sounds really great. I guess it’s just another example of how Microsoft is using the open source model these days. You said that there is an open source compiler – how is that different from what is included in Visual Studio and the Visual F# that you were talking about?
Phillip Carter: [00:45:34.09] The current model in which ways are developed is there are two compilers: the open source one and the Visual F# compiler. All current development today happens in the open source Visual F# compiler, and changes that are made there are typically backported into the open version.
The open version is maintained for a few different reasons. If there are any different priorities that the community might have, or that the organization might have from Microsoft, and they say, “You know, 99% of what you’re doing is great, but this one little thing, we think we want to do it this way”, they’re enabled to do that with the open source version of the compiler. Although there’s not really much of a difference between the open F# and Visual F#, actually really the only differences these days are that Visual F# has a little bit more features in it at a point in time, because active development is happening there. It enables the community, it empowers them to have an open version of the language that they can work with and organize around. That’s how we have the open compiler and the Visual F# compiler.
[00:46:38.26] It’s a bit interesting to call it the open compiler, because they are both open and they are both open source. That wasn’t necessarily true in the past, when Visual F# was closed sourced, but Visual F# was actually one of the first things that Microsoft began open sourcing and doing open sourcing and doing open source for. Because there was already this open edition of everything it was very easy to just say, “It works over there, so we’ll just do it here.”
In typical F# fashion, we pioneered, in a way, and a lot of that is following suit. Now, almost all major activity in the .NET ecosystem is happening open source on GitHub. It’s open development, as well.
[00:47:19.03] The model that we have, where community members can make really big and empowering changes – they can do that across all of .NET now. That’s just better for everybody.
Eberhard Wolff: [00:47:31.18] So I could do a pull request on the Visual F# compiler?
Phillip Carter: [00:47:36.01] Absolutely. There’s nothing stopping you from doing that. In fact, if you were to go there right now, you’ll probably find — I don’t remember what the number was, but there’s pages of pull requests right now, and they’re all under review. Sometimes they’re under review for a long time, because the change is a really challenging one, and it requires months of feedback to get it right, but you could absolutely do it right now.
We merge a couple of them every single day, and any new stuff that we do with the tooling on Microsoft’s side, we just create as pull requests too, and the community members go in there and engage.
Sometimes they notice things that we did wrong and they say, “Why is it this way?” and we say, “Oh, yeah… Well, we didn’t really think of that.” It’s a really cool relationship that we have there.
Eberhard Wolff: [00:48:18.09] That’s definitely the power of open source. What is the open source license that you’re using for Visual F#?
Phillip Carter: [00:48:27.06] Apache license. It’s Apache version 2.
Eberhard Wolff: [00:48:30.12] Okay, so it’s one of those liberal, [unintelligible 00:48:31.19] licenses.
Phillip Carter: [00:48:36.29] Yes. You can take this language off of GitHub, and you can do anything you want. You could fork it, you could make your own version of F# that is totally different, no problem there. So long as you just attribute as acquired, then it’s all good to go. In fact, some people even do that. They have their own forks of F#, where they might have a particular feature that they like, and they use that. You can do effectively what you want here.
Eberhard Wolff: [00:49:00.26] What would be the easiest way to get started with F# development?
Phillip Carter: [00:49:05.29] The easiest way to get started with F# is to go to Fsharp.org. There are a lot of resources there; there is a little Learn tab there, so you can learn about the language F#, various resources for learning. There are a lot of blogs, a lot of guides, various places with documentation – there’s our Microsoft Visual F# documentation… All of this is listed on this website here, with all the different ways that you can learn it.
[00:49:29.14] Then there are ways that you can try it out, like the Try F# Interactive Environment in your browser, and there’s of course Visual Studio. If you download Visual Studio and under Additional Components you select Visual F#, you can get F# in your development environment and then you can just start hacking on the language.
[00:49:47.14] There are a number of books as well, such as “F# for C# developers”, “Real-World Functional Programming”, “Functional Programming Using F#.” A number of these are available on Amazon, and all of this stuff is listed on the Fsharp.org website. If you were to go to the website, you would probably get all of the information that you would need to get going with using F# today.
Eberhard Wolff: [00:50:11.09] Any other things that you want to mention, any questions that I didn’t ask you?
Phillip Carter: [00:50:18.07] I would just encourage people, especially if you’ve never looked at functional programming before – maybe you’ve heard about it, maybe you haven’t – and you said, “I wonder what that is…?”, dip your toes into functional programming a little bit. Try F#, try Scala, Haskell, OCaml. Try all these different languages. If anything, it can help you become a better programmer, because it will get you to think about things in a little different way.
[00:50:44.03] I encourage people to get started with functional programming. If F# is the language that you choose to learn functional programming, then I’ll be even happier.
Eberhard Wolff: [00:50:55.28] I totally agree. It’s really important to learn functional programming. Not just because it’s a hype right now, but just to become a better programmer [unintelligible 00:51:03.11].
Thanks a lot. Thanks a lot for taking the time and being on the show. Have a lot of success with F#!
Phillip Carter: [00:51:14.19] Thank you so much for having me. This is splendid!
[…] SE-Radio Episode 269: Phillip Carter on F# […]
I was looking for a podcast with actual solid content that I could grow my knowledge from. You guys delivered! Working my way through the backlog but felt compelled to say I really enjoyed this one with some Microsoft content. As an enterprise dev in government Microsoft plays a big role in my career. Anyway, really enjoyed this one, please do more maybe on latest c# language features or docker in Azure.
Great podcast, thank you for sharing!