.NET: A look through F# lenses
- select the contributor at the end of the page -
While Ruby and its frameworks, Rails and Sinatra, are popular for programming Web apps, there’s also a significant segment of the world that relies on the .NET platform. The .NET platform is a general purpose programming platform that can do more than object-oriented programming (although it does that, too). The foundation of the .NET platform is s Common Language Infrastructure (CLI).
The infrastructure of the .NET platform includes a rich set of libraries that all .NET languages can access. Because of this it’s highly recommended that you apply the .NET library design guidelines for programming languages like F# and C#, which both use the .NET framework. As an example (in F#): For events, namespaces, interface types and methods, properties, fields, exceptions—use PascalCase. Here’s an example of that from an F# demo program that demonstrates I/O, where you can see the use of PascalCase in the function declarations, let user = Console.ReadLine() and let noob = Console.ReadLine() .Trim():
As you can see in the above code snippet, you can create your own namespaces and modules within the rules of the .NET library guidelines and also within the syntactic rules of F# -- such as the files Idiomatic.fs and Conditionals.fs, shown above in the Xamarin IDE’s file tree. Then you can access the code in those files from any other source code file in your project, including the main Program.fs file.
As mentioned, the .NET library has classes that every .NET language can access. Here’s a screencap from my Xamarin Mac IDE showing this relationship with the F# Sequence module and the .NET IEnumerable class.
You can find more examples of .NET library design guidelines (that are F#-specific) in Don Syme’s book, “Expert F# 3.0” (flip straight to page 575), as well as the MSDN libraries site.
The garbage collector
The .NET garbage collector automatically frees up any memory that’s no longer referenced in a program. Usually when an execution leaves the function scope, the value is no longer accessible, so the memory occupied by that object is marked for deletion and will be freed up later by the garbage collector. This is very helpful for managed resources, referenced by the CLI.
The CLI is the foundation of the .NET platform. It’s an open specification developed by Microsoft and standardized by ECMA and the ISO. These standards take into account the unique characteristics of different system environments by ensuring that different high-level languages can be executed in different environments.
The ECMA Standard consists of several parts:
- Concepts and Architecture
- Metadata and Semantics
- CIL Instruction Set: This is a platform-neutral language.
- Profile and Libraries: Provides an overview of the CLI libraries and a specification of their factoring into Profiles and Libraries. A compression file, CLILibrary.xml, is considered to be part of the Profile and Libraries partition. It is distributed in XML format and provides details for each class, value type and interface in the CLI libraries.
- Debug Interchange Format: Describes a standard way to interchange debugging information.
- Annexes: Contains some sample programs written in CIL Assembly Language (ILAsm) and a machine-readable designation of the CIL Instruction Set, which can be used by tools that manipulate the CIL
.NET-compatible languages (F#, C#, and Visual Basic, to name a few) compile to a CIL, which is part of the CLI infrastructure.
The .NET framework contains libraries for everything from databases to Web services and graphics. F# can take advantage of these libraries and any F# code can be consumed by other .NET languages like C#. F# supports the full cast and crew of primitive .NET types. This means that F# apps can run on any platform that supports .NET and can be used as a great data-rich backend that plays nicely with a C# Web app’s interface (or even used as an F#-only web app, thanks to tools like WebSharper).
The way memory works in the .NET environment is a relatively simple concept, but it’s something that .NET developers must keep an eye on. Values in .NET apps are stored in either one of two locations: the stack or the heap.
The stack is a fixed amount of memory for each process where local variables are stored. Local variables are temporary values used only for the duration of a particular function’s execution. Examples would include something like an incrementor or a loop counter. The stack is smaller than the heap and has a limited amount of space for the storing of values, whereas the heap (also known as RAM) is significantly larger.
.NET uses both the heap and the stack. And depending on where a value is stored, will determine how you can use that value. The values stored on the stack are called value types, and the values stored on the heap are called reference types. But all of this means very little without some concrete context in which to understand these concepts, so let’s look at how .NET works by introducing my favorite programming language, F#. We’ll also discuss the tools available to you outside of Visual Studio if you’re on a Mac or Linux operating system.
Pure object-oriented programming languages like Ruby are great, but they have one major shortcoming: They don’t handle the coding of complex algorithms very easily. If you ever run into that situation, you’ll see why F# as a functional-first programming language is a great tool to add to your developer’s kit.
If you ever have to write a parser that could handle a clobbered YAML file that Ruby’s Nokogiri gem will choke on, and even PHP’s cache of tools may fail, F# is a great tool for the job. A senior developer introduced me to F# and showed me how to write the parser for that exact situation using F#. Here’s a code snippet from that F# parser program:
Seeing how well F# handled that clobbered YAML file task with F# pattern-matching capabilities, I observed that F# would also be useful in solving the kinds of problems I care most about: Accurate data-gathering and analyzing for everything from improving railroad crossing safety signals, redesigning traffic-flow and traffic light synchronization, freight management and transshipment, to name just a few. It also provides accurate information-based Domain Driven Development (also known as Type Driven Development) solutions for many things.
Here’s a code snippet and terminal output from part of a program I’m building. It’s modeled like a call tree for people to access either by phone or computer for reporting their outcomes of trying to get helped from NGO’s. The program then writes that data on the help request outcomes to a JSON file that will serve as a streamed data feed which can then be accessed by API’s. The data is then analyzed, sorted and packaged in reports that can be used by others for making policy recommendations to lawmakers, as well as to private and public organizations.
The functional architecture of F#
F# is a Turing-complete language. It’s a statically typed language, and as such it has far better data structures than C#. An example of that is the Discriminated Union type (seen in the parser program snippet above in the type YamlObject). F# Discriminated Union types, or DU for short, and Record types are immutable by default. They have structural equality and don’t allow nulls, meaning it’s much easier to trust the integrity of the data and easier to compare and manipulate it. From a functional programming standpoint, there are only two types of operations you should write: commands and queries. A command has side effects and causes the state of the system to change. A query returns data. Each operation you code should only do one or the other, not both. This is known as the Command-Query Separation Principle.
The Command-Query Separation Principle
- When you receive input for a command, you can’t return data.
- All commands in this architecture are potentially asynchronous.
- When you want to accept a command, you must not return a response.
- Results are communicated by asynchronous notifications, or tokens for status updates.
An excellent Pluralsight video tutorial demonstrating an actual online booking app shown by Mark Seeman illustrates the concepts of concurrency, consistency, synchronous queries, data entry and asynchronous notifications.
.NET programming on a Linux or Mac
Almost every available F#, C# and Visual Basic (VB.NET) tutorial is presented by developers who use Visual Studio because they’re on the Windows OS. Currently, Visual Studio is largely unavailable outside of Windows, nor does it play nice with Linux or Mac. But Xamarin and Monodevelop, the free open source version of Xamarin, are excellent tools available to Mac and Linux users.
You can download and install F# (and C#) and all of the components you’ll need for creating beautiful F#, C#, and VB.NET programs by going here for Monodevelop if you’re a Linux user, and here for Xamarin if you’re a Mac user. These tools have better syntax highlighting than Visual Studio and are enjoyable for Linux and Mac diehards. Most Visual Studio users prefer the sluggish Visual Studio IDE, with its GUI, to working in the Windows terminal. This isn’t much improved, even with the use of Cygwin, although some improvements are now being made with a tool that you can download and install from Github.
With Xamarin and Monodevelop, you have access to the .NET reference libraries’ packages and some cool F#-specific packages too.
Note: For Mac users on Mac OS X Yosemite and El Capitan, Xamarin might act quirky when you select “Run” to compile and run your code. You may get a terminal output with a weird message that says “read: -p no coprocess” like this:
Don’t panic if this happens. It doesn’t mean that your program isn’t compiling and working correctly. It’s just a quirky behavior that the IDE sometimes has with Mac—it does not happen to everyone, but does for many. If you go to your project’s directory in your terminal and cd into the bin/Debug directory and run the command mono <filename>.exe, you’ll see that your program compiled just fine and that the annoying ‘no coprocess’ message does not show up:
Feel free to do a sanity check by making sure your compiler is installed and is the most updated version by running the command gcc –v, followed by the commands fsharpi and fsharpc, respectively.
As you can see, everything works. Xamarin and Monodevelop behave differently than Visual Studio in other ways as well. For example, in order for it to compile correctly, the code for including a scripting reference in F# must be written slightly differently in Xamarin and Monodevelop than it is in Visual Studio. In Visual Studio, you would write a scripting reference as #r @“packages/../../FSharp.Data.dll”. Tomas Petricek does this in his F# tutorial, Accessing Data With F# Type Providers, by showing an example of how to build a program using API’s in F# to write a data-mining a web crawler to retrieve information about popular movies on Netflix, like so:
But if you try to write the code for script referencing like this in Xamarin or Monodevelop, you’ll likely get a parser error:
These little things can trip you up when you’re taking a course and the presenter is using a different OS and a different IDE. This is why you should always test your code snippets in the F# interactive, which you can access either directly via your terminal (by running the fsharpi command) or in the F# Interactive window that comes with Visual Studio, Xamarin and Monodevelop.
With F# script files, you don’t know if all the code will compile, because you don’t compile it. You run snippets of your code in F# Interactive and test it that way. Then you can move your code into a Library file, and from there you can access it in your F# project. The project’s entry point, [<EntryPoint>], can be either an F# or a C# solution file. Since C# and F# are both .NET languages, the CLI allows for this sort of “resources-sharing” between these two languages.
.NET and F# namespaces and modules
In F# the .NET namespace System.IO contains primary .NET types for reading and writing bytes and text to and from data sources. So, the .NET platform not only makes working with files and libraries easy, it also makes it easy to create complex data models and databases from plain text, JSON, XML, and CSV files. Several .NET I/O streaming tools are available to you for processing and analyzing raw data in this way.
Here are are just a few .NET namespaces you’ll see used quite often:
System.IO.BinaryWriter: Writes primitive data types as binary values, you can create output streams by using File.Create(filename).
System.IO.StreamWriter: Writes text strings and characters to a stream.
System.IO.StringWriter: Writes text strings to a StringBuilder, which eventually can be used to generate a string.
System.IO.TextWriter: Writes text strings and characters to an unspecified output, a common functionality implemented by StreamReader and StreamWriter types, and System.Console.ReadLine() and System.Console.WriteLine()
An example of a real world application of gathering and writing raw data can be found in generating raw data that’s accurate for processing and analyzing by taking input from users. This is then captured and written to a JSON or XML file (or data feed), for eventual use in improved policymaking where the directing of funding should go (and where it probably should not go). Here’s a code snippet from part of my own program that writes the captured data from user input to a JSON file:
The use of StreamWriter in conjunction with the serializer and deserializer functions writes the captured data from user input and writes it to a JSON file which will be further processed. Here’s what an examination of that JSON file looks like:
Domain-Driven (or Type-Driven) Development
You’ll get the most benefit out of a strongly typed, and statically typed language like F# with Domain-Driven (or Type-Driven) Development. The way you model your data, and the way you represent that data in your types, dictates the flow of the rest of the app. If you take your time in the beginning with modeling your data accurately, you’ll save yourself a ton of headaches further down the line. Here’s where it helps to make a chart, drawing the steps of what happens and what the likely outcomes are at each step:
It looks complicated, but F# and its access to the full .NET library gives us the tools to set up our types to match this data model. It’s also worth noting that, unlike other .NET languages, F# is the only one that has Discriminated Unions (or DU’s) as one of its types. The DU is a powerful tool for data representation, and works well with another F# tool: pattern-matching.
Note that we can define an ordinary .NET class in F# by adding members to the DU’s, which would then be easily accessible to C# (which would “see” the DU as a static class). When compiled to .NET code, the DU type’s member becomes just an ordinary C#-friendly property. The type name is followed by a parametered list, which creates a primary constructor for the .NET class. Here’s an example of providing a C#-friendly interface in F#.
The rest of the code defines two read-only properties using the member keywords. The x.Vacancy is a Boolean which is true when a Safehouse has a vacancy available. The Vacancy property returns the actual Safehouse with a vacancy, or fails when it’s missing. This particular way of coding a type in F# is not as safe as an F#-only option type. But, it will keep the C# developers happy because it makes it easier for them to work with since it involves less coding gymnastics for the C# code to access the F# code, along with the more complex functional data manipulation and complex mathematical computations (like, for example, a Mandelbrot set).
Now that you have an overview of .NET and F#, don’t be afraid to dig around and search for more information. Go ahead and dive in with what you already know, and stay tuned for more.