Chapter 4. Comparing Approaches for Programming

Up until this point, we have centered upon the C# language and its evolution. However, this evolution is not the only one in .NET framework as far as languages are concerned. Other languages have kept evolving as well (and this is independent of the fact that many more compilers have increased the list of languages for which there is a .NET version nowadays). Especially, there are two members of the .NET language ecosystem, F# and TypeScript, which are becoming increasingly popular among the programmer community, and we're going to—briefly—introduce them in this chapter.

Consequently, we will review some of the most relevant aspects of both languages, with C# as a reference in both cases.

With this goal, our purpose is to roughly underline the most crucial programming structures in such a way that you can establish a comparison between the ways you code usual and everyday programming tasks but using different languages.

I'd like to add a note about VB.NET and why I'm not including it here. Even considering that VB.NET has evolved in parallel with the rest of the languages of the .NET ecosystem, (and VB.NET followers, please forgive me, but this is a tendency that most of you must have noticed for sure), the truth is that there's more hype around F# and JavaScript languages (and TypeScript, for that matter).

That tendency is especially clear when we talk about future expectations. VB.NET users can be confident that while Microsoft doesn't say the opposite, VB.NET will keep on running as expected, and will be a language of the previously mentioned language's ecosystem, with all the goodness and advantages that a developer is used to find inside Visual Studio.

Going back to F# and TypeScript, both have something in common: they belong to the category of functional languages (although TypeScript uses classes, which later on "compile" to functions). The first one will serve us as a comparison with C#, since it's a compiled language, unlike TypeScript, which is an interpreted language.

Note

The list of functional languages of all types keeps on growing. Wikipedia maintains a pretty comprehensive list of most of them at https://en.wikipedia.org/wiki/Category:Functional_languages.

So, we'll review the differences between both approaches to programming and identify those programming structures that are intended to obtain the same results, each one expressed in its own style. Also, note that there are a number of applications out there to solve problems (especially mathematical and engineering ones) with an implementation of an F# DLL, since possibilities increase with every version as the language grows.

On the other hand, the continuous growth of TypeScript adoption has been clearly stated with the announcement by Google that TypeScript has been the language used in the building of their new version of the Angular framework (Angular 2.0), available since September 2016. That's a quite unexpected collaboration!

In this chapter, we will cover the following topics:

  • Foundations of functional languages
  • F# as a fully functional language
  • Equivalence of typical programming structures
  • Support in Visual Studio
  • TypeScript inception and main purposes
  • Basic structures and programming characteristics
  • TypeScript support in Visual Studio

Functional languages

A functional language is one that avoids changing the state and mutable data and mainly focuses its statements on the evaluation of code as mathematical functions. So, the entire programming experience is based on functions (or procedures) to construct its program flow.

Note how different this approach is from object-oriented languages for whom everything is an object (some OOP languages have primitive types, such as Java, but, in general, types can always be considered objects).

The next graphic shows some popular Imperative Languages as opposed to pure Functional Languages and the place F# (and Scala) occupy between the two models.

With that in mind, the flow of the program's goal keeps declaring functions that relate or base themselves in other functions until the goal is reached:

Functional languages

A functional language does not have the side-effects of other imperative programming languages since the state does not change and the same call will return the same results as long as the function calls are made with the same arguments. Eliminating these kinds of side-effects can make the behavior much more predictive, and that's precisely one of the most important motivations for their use.

However, this advantage also implies a consideration: not all programs can be developed without these effects, especially those that require changing the state and the creation of I/O procedures.

Functional programming, as a discipline, has its roots in Lambda Calculus, originally developed as a formal system to express computation in terms of function abstractions, and it was first developed by Alonzo Church in the late twenties in the context of research on the foundations of mathematics.

Two points should be noted here:

  1. Lambda Calculus is a universal model of computation equivalent to the Turing Machine, as stated by Wikipedia. Furthermore, Turing himself refers to this work in his seminal paper on State Machines. There are interesting works that relate to both and explain their differences, although they're not the goal of this chapter.
  2. Even considering they're not the same as the lambda expressions we've seen so far in the C# language, there's a direct connection given that the Lambda Terms or Lambda Expressions used in Lambda Calculus refer to binding a variable in a function pretty much in the way we've seen in previous examples. For more information on Lambda Calculus, refer to https://en.wikipedia.org/wiki/Lambda_calculus.

F# 4 and .NET Framework

The first version of the F# language showed up in 2005, although it was something that you had to download and install separately. The language evolution process was originally managed by Don Syme from Microsoft Research, although nowadays, it uses an open development and engineering process, basically monitored by the F# Software Foundation, founded in 2013, which became a 501C3 nonprofit organization in 2014.

Actually, the current version is 4.0, and it appeared in January 2016 along with several improvements in the tooling linked to Visual Studio. However, support for F# programming can be found also in Xamarin Studio, Mono, MonoDevelop, SharpDevelop, MBrace, and WebSharper.

According to Syme, F# originated from ML and has influences from other functional languages, mainly from OCaml, C#, Python, Haskell, Scala, and Erlang. To be more precise, Syme explains that from the syntactic point of view, the main influence comes from OCaml, while the object model aspect is inspired from C# and .NET.

F# is defined as a strongly typed, multi-paradigm, and functional-first programming language that encompasses functional, imperative, and object-oriented programming techniques.

It uses type inference from its inception in the way we've already seen from version 3.0 in C# when using the var keyword. However, F# admits explicit type annotations, and it requires it in certain situations. However, exceptions apart, every expression in F# is linked to a static type. If the function or expression doesn't return any value, the return type is named unit.

The inevitable Hello World demo

So, let's start with the mandatory Hello World program, but first, keep in mind that you will have to activate F# tools in Visual Studio if you did not do that at installation time. (If you didn't, when accessing the F# language section, you'll be offered this activation.) Once activation is ready, you will be presented with a new type of project linked to this language in the way shown in the following screenshot:

The inevitable Hello World demo

To start with, you can pick up the tutorial, which is a comprehensive collection of the different aspects of the language— it's pretty well structured—or just use a console application for that matter.

If you opt for the tutorial, you can mark a section of the code you want to test, and if you right-click on that area, you'll be presented with two options for execution: Execute In Interactive and Debug In Interactive:

The inevitable Hello World demo

To visualize the results, we can—of course—create an executable and launch it in a console window, but for this scenario, F# Interactive Window is more suitable. For completeness, keep in mind that you can call the compiler (named fsc.exe) using the build option in Visual Studio just like you would with any other project.

So, we'll start with writing very simple code:

let a = 1 + 2
let b = a * 3
printfn "Expression a equals to %i" a
printfn "Expression b equals to %i" b
printfn "So, expression b (%i) depends on a (%i)" b a

This generates the following output in the F# Interactive window:

The inevitable Hello World demo

Here, we've done the following:

  1. First, we used the let keyword in order to assign an arithmetic expression to variable a.
  2. Then, we used the same keyword for another variable, variable b, which uses the previous definition.
  3. Finally, the printfn function of the F# library produced an output to the standard I/O, formatting the result in a manner similar to what we do in C#, just changing the {0}..{1} and so on, expressions into %i or the letter corresponding to the type of the value to be printed (f for float, s for strings, d for double, A for generic printing including arrays and tuples, and so on).

Note how the interactive window presents an echo of the members implied in the process, indicating its type as well.

Keep in mind that since F# is considered another member of the Visual Studio family, it has its own area of configuration in the Tools/Properties dialog box, as shown in the next screenshot. In addition, another entry in this dialog allows you to configure behaviors of the F# Interactive window:

The inevitable Hello World demo

Identifiers and scope

The scope of an identifier indicates the area of code where such an identifier is usable. All identifiers, either functions or values, are scoped (valid) starting at the end of their definition until the end of the section in which they act. Additionally, in F#, you don't have to explicitly return any value, since the result of the computation is automatically assigned the corresponding identifier.

So, in order to create intermediate values for a computation, you denote it with indentation (by convention, the size of the indentation is 4, but the user can opt for any other value). Every indentation defines a new scope, and the end of every scope is marked by the end of the indentation. For example, let's think of a function to calculate the hypotenuse of a right triangle:

let hypo (x:float) (y:float) =
  let legsSquare = x*x + y*y
// System.Math.Sqrt(legsSquare)
sqrt(legsSquare) // an alias of the former
printfn "The hypotenuse for legs 3 and 4 is: %f" (hypo 3.0 4.0)

First, we define the hypo function, which receives two arguments of type float (note that we can explicitly indicate types with the syntax arg:type). Then, we declare legsSquare to be the sum of the squares of both triangle's legs, and this is done with a new level of indentation. Finally, we just calculate the square root of this intermediate variable without any other indication, which means that System.Math.Sqrt(legsSquare) is the return value.

Also, note how we make a reference to System.Math directly since it's included in the references of the project. Alternatively, you can use the open keyword to indicate a reference to System (such as open System), and then, you can use the Math static class in an unqualified way.

Finally, we call printfn to format the output and make sure there's only one value to include in the formatted output string by enclosing the call to hypo in the parenthesis.

Of course, the F# Interactive window will show the formatted result, as is expected:

Identifiers and scope

Lists

In F#, lists are ordered collections of immutable elements of the same type. Its generation admits many language-compatible options, but the initial style is very simple. If you go through the tutorial a bit, you'll discover the following declaration:

/// A list of the numbers from 0 to 99
let sampleNumbers = [ 0 .. 99 ]

Here, you are introduced to the range operator: (..), which denotes a sequence of elements deductible for the compiler. Due to the echo feature of the Interactive window, we just have to mark that line and select Interactive Window to view the results:

Lists

The range operator also admits the increment descriptor (..), allowing you to define a different, collective type, called sequence (seq) that supports many of the same functions as lists:

seq {5..3..15}

Here, we define a sequence of elements starting with 5, going through 15, and increasing in steps of three (the output being the following):

val it : seq<int> = seq [5; 8; 11; 14]

In the same manner as earlier, let's mark the following sentence and proceed:

let sampleTableOfSquares = [ for i in 0 .. 99 -> (i, i*i) ]

An array of tuples is created, each containing a number from 0 to 99 and its corresponding squares. There are a few things to notice in this sentence.

Assigning something in squared brackets means that the expression inside it should be evaluated to an array (similar to JavaScript). We've seen this in the previous sentence as well, but this time, inside the brackets, we see a for..in loop and a -> symbol, which indicate the generated value for each step in the loop.

This generated value turns out to be a tuple, since it's enclosed in parenthesis—a very powerful and expressive syntax.

List objects contain several useful methods to manipulate their inner collections. One of them is List.map, which permits you to apply a function to each element in the list and returns a new list with the calculated results.

It's important to note how close the philosophy of this approach reminds what we saw in C# when using generic's collections and passing a lambda expression to their methods.

For example, we can write the following:

let initialList = [9.0; 4.0; 1.0]
let sqrootList = List.map (fun x -> sqrt(x)) initialList

printfn "Square root list: %A" sqrootList

Here, initialList contains three float numbers, for which we want to calculate the square root. So, we generate another list (sqrootList) by calling List.map and passing to it as an argument an anonymous function that gets a value and returns its square root. The last argument is initialList. Note that the argument of map is just like a lambda expression.

Once more, the output in the F# Interactive windows is as expected:

Lists

Function declarations

We've seen how to use anonymous functions and how they can be passed as arguments to some methods. To declare a named function, you can use a similar syntax as what you used with variables but by specifying the arguments it can receive, following the function's name:

let func1 x = x*x + 3

This time, func1 is the name of the function, and x the argument to be passed, which can, optionally, be enclosed in parenthesis. Later on, we can combine the previous declaration with an assignment to a variable, which you can see if you continue to analyze this code:

let result1 = func1 4573
printfn "The result of squaring the integer 4573 and adding 3 is %d" result1

Also, remember that arguments can be annotated (indicating its type explicitly):

let func2 (x:int) = 2*x*x - x/5 + 3

When we need to indicate code structures inside the function, we will use indentation the way we saw in a previous sample along with the required expression to be evaluated (and that's what func3 does here):

let func3 x =
  if x < 100.0 then
    2.0*x*x - x/5.0 + 3.0
  else
    2.0*x*x + x/5.0 - 37.0

Note the difference between lists in relation with tuples, which are defined enclosed in parenthesis. Furthermore, a list can be generated using expressions that include loops, as you can see in the following code, which uses such a construction:

let daysList =
  [ for month in 1 .. 12 do
  for day in 1 .. System.DateTime.DaysInMonth(2012, month) do
    yield System.DateTime(2012, month, day) ]

Here, we have two for loops nested using the for..in..do syntax. Also, note the presence of the yield keyword, just like in C#. The previous code generates the following output:

Function declarations

And the list goes on...

The pipeline operator

Another important feature of F# is the pipeline operator (|>). It serves to pass a value to a function but by piping the result. The definition is simple:

let (|>) x f = f x

This means: take a parameter x and apply it to function f. In this way, the parameter is passed before the function and we can express chains of calculations in a very suitable manner. For instance, we can define a sum function and use it this way:

let sum a b = a + b
let chainOfSums = sum 1 2 |> sum 3 |> sum 4

After the execution, we'll obtain a value of 10 for chainOfSums. For example, consider that we have the following code:

let numberList = [ 1 .. 1000 ]  /// list of integers from 1 to 1000
let squares = numberList
  |> List.map (fun x -> x*x)

This means, take numberList, apply it to List.map of the function enclosed in parentheses (which calculates the square of every number in the list), and assign the result to the squares identifier.

If we need to link several operations in a sequence over the same collection, we can concatenate the operator, as the next sample in the tutorial shows:

/// Computes the sum of the squares of the numbers divisible by 3.
let sumOfSquares = numberList
|> List.filter (fun x -> x % 3 = 0)
|> List.sumBy (fun x -> x * x)

Pattern matching

Another useful structure when managing collections is the match..with construct. It's used with the | operator to indicate different options, much like what we would do in a switch and case statement in C#.

To test this in simple way, let's try the typical recursive function to calculate the factorial of a number:

let rec factorial n = match n with
  | 0 -> 1
  | _ -> n * factorial (n - 1)
let factorial5 = factorial 5

Note the presence of the rec keyword as a modifier of the function's declaration in order to indicate that the function is recursive. The variable to test (n) goes in between the match..with structure and the | operator separates the distinct cases to be tested. Also, look at how the underscore operator (_) is used to indicate what to do with any other value.

The F# Interactive window, once more, will show the correct results:

Pattern matching

Classes and types

F# can work well with classes and types, and by itself, it is considered a typed language. Actually, you can declare elements of any supported type using type abbreviations and type definitions.

In the first case, we just establish aliases of existing types. You can declare these aliases using the type keyword followed by an identifier (the alias) and assign it to the type it represents:

type numberOfEntries = int
type response = string * int

Note that the second definition declares a new type construct made of two parts (string and int) that can be used later on.

The most common usage of types is to declare type definitions, indicating members as pairs of values (key : type) enclosed in curly braces, as the RecordTypes module of the tutorial reminds us (we reproduce it here):

// define a record type
type ContactCard =
  { Name     : string;
    Phone    : string;
    Verified : bool }
  let contact1 = { Name = "Alf" ; Phone = "(206) 555-0157" ; Verified = false }

If we launch the previous code in the F# Interactive window, we will get the following:

Classes and types

As you can see, the contact1 identifier is qualified as val (value). This value can also be used to declare further types based on the previous one. This is the purpose of the following declaration in the tutorial:

let contact2 = { contact1 with Phone = "(206) 555-0112"; Verified = true }

In this case, contact2 is based on contact1, but there are two changed values.

The next definition makes things quite clear as well:

/// Converts a 'ContactCard' object to a string
let showCard c =
  c.Name + " Phone: " + c.Phone + (if not c.Verified then " (unverified)" else "")

This is used to convert the ContactCard type into a string as we check the output in the F# Interactive window after adding another sentence to test the output:

let stringifyedCard = showCard contact1

The following screenshot shows the output generated:

Classes and types

Casting

In F#, casting sentences have their own specific operators. The two possible operations depend on the type of casting required (downcasting if you go from general to particular in the hierarchy, or upcasting if you proceed in the opposite way). As it happens, with C#, the hierarchy starts with System.Object, although there is an alias, obj, which we can use in its place.

As for the behavior in relation to the compiler, let's remember that upcasting is a safe operation because the compiler always knows the ancestors of the type to be converted. It is represented by a colon, followed by a greater-than sign (:>). For example, the following operation is legitimate:

Casting

As we can see, the someObject identifier is declared in parenthesis as the result of a conversion from a literal string to obj. As a result, in the next sentence, Intellisense in V. Studio reminds us that System.Object declares exactly those members that appear in the contextual menu.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.135.247.181