F# (pronounced as F sharp) is an open source programming multiparadigm language introduced for the first time by Microsoft in Visual Studio 2010. F# is a first class member of the .NET Framework languages and derives from the ML family of functional languages. F# supports the functional paradigm in addition to the traditional object-oriented and imperative paradigm of the Microsoft .NET Framework. Microsoft Visual F# is the real language implementation of Visual Studio. In the previous chapters, we have already seen some examples of functional programming (the Functional programming section of Chapter 1, First Steps Toward Reactive Programming ) written in C# using Reactive Programming concepts.
In the next two chapters, we will extend the information combining F# and Rx to introduce and understand Functional Reactive Programming (FRP).
In particular, in this chapter, we will see the following topics:
When we use .NET Framework, the first thought goes to languages such as C# and VB.NET. If you want to write a program or services using a functional paradigm, probably your choice would include other languages, for example, Erlang, Haskell, Scala, Wolframe Language (Mathematica), and so on.
This occurs for a variety of reasons, such as the following:
Contrary to the reasons just written, F# is a very interesting language, because it combines the potential of the .NET Framework with a simple syntax and the functional paradigm.
F# is different from the C# and VB.NET languages. This is mainly due to the basic paradigm.
The principal features of F# are as follows:
Moreover, writing functions in F# is very simple and advantageous because the syntax is very concise:
let sum a b = a + b let add5 = sum 5 let result = add5 2 // the result value is 7 let result2 = add5 3 // the result value is 8
As you can see in the first row of the previous example, a sum
function is declared between two values (a
, b
). In the second row, the function add5
is equivalent to sum 5
, where 5
is the value of the parameter a
. The add5
variable is a partial function because we have to declare the second parameter if we want to execute sum
, as we can see in result
and result2
:
[0..100] |> List.map (fun x -> x * x) |> List.iter (fun y -> printfn "the value is: %i " y)
Instead, in the preceding example, you have more instructions concatenated with the |>
(pipe forward) operator. In particular, declare a list of int
. After this, for each value, it is applied to the square and it returns a new list. In the end, through List.iter
, print a visual result value.
By using C# and object-oriented programming (OOP), the code to write these functions will be bigger than in F#. Instead, by using the functional paradigm with C#, the result would be more or less the same.
The functions iter
and map
are also a useful example to introduce the FRP.
One of the most important features of F# is the immutability. This is fundamental to writing a function that respects the algebraic principles:
//C# code int x = 5; x = x + 1;
In mathematics, no value attributed to x
could solve the function x = x + 1
. In F#, writing this code directly, without using the keyword mutable
, would generate the following error:
//F# code: let x = 5 x <- x + 1 error FS0027: This value is not mutable
To execute this code correctly, it will be sufficient to change the declaration of x
in the following way:
let mutable x = 6
The result of the operation using F# Interactive
is as follows:
They both allow you to run F # code and the file .fsx
(F# script).
Performing the code is simple: you just have to select rows of interest and press Alt + Enter or right-click and use the command Execute In Interactive
. The selected text will be executed by generating a result or an exception.
Instead, if you want to run script files, it's preferable to use Fsi.exe
from the windows console by specifying the file path; in this way, you will get the result in real time in the output window.
Another important feature of the language is its ability to interpret the types. In reality, this is not a real characteristic, but a technique for the system to deduce the type.
Indeed, F# has a powerful type inference system. Take a look at the following code:
//declare a tuple let tupleValues = 1, "one" //unpack values let v1, v2 = tupleValues let sumfloat values = List.reduce (+) values printfn "The sum is: %A" (sumfloat [4.6; 10.3])
List.reduce
is a function applicable to a list of values and reduces two elements of a collection to a single one. In the preceding example, the function List.reduce (+) values
adds the first two elements of the collection. Then, the result is processed with the third element and so on, until the final result is obtained.
The same operation could be written using the operator pipe forward:
values |> List.reduce (fun x y -> x + y)
or
values |> List.reduce (+)
In this example, in the first four lines, we have declared a tuple
from which individual values are extracted. The result in the console F # Interactive is the following:
val tupleValues : int * string = (1, "one") val v2 : string = "one" val v1 : int = 1
Due to the inherent ability to infer the type, the compiler links v1
and v2
with the correct type, respectively, int
and string. Similarly, for the subsequent lines, the first use of the function sumfloat
is deduced by the declaration of the float
type items of the list [4.6
; 10.3]
. The result is as follows:
The sum is: 14.9 val sumfloat : values:float list -> float
The title of this section is not easy to understand. It is true that the functions are the basis of F# and its paradigm, but first class values does not mean anything.
However, to make sense of these words, you need to think in functional programming sense.
The key to understand this is much easier than you might think.
F# is a functional language and, as such, it is functional-oriented.
This is one main difference compared to the other .NET languages, which are object-oriented.
Moreover, if one considers the functions in F#, they are everywhere. All of the examples, even the rows, done so far are functions!
Even the simplest of instructions, let x = 5
, is a function.
For correctness, it is good to talk about identities and not pure values. In fact, when you declare an identifier assigning a value, it can no longer be changed.
Being functional-oriented forces F# to comply with certain rules and to have some benefits and drawbacks.
The rules are as follows:
The benefits are as follows:
The drawbacks are as follows:
Naturally, these features are valid only in a functional context. By declaring an identifier using the keyword mutable, as seen previously, you can change the value of the identifier itself.
F# is not only a functional programming language. This is a fundamental principle to remember. By definition, F# is a programming language that provides support for functional programming in addition to traditional object-oriented and imperative (procedural) programming.
If you want object-oriented programming in F#, the language makes all the necessary constructs available. One of the main types of object-oriented programming is classes.
The syntax to declare a class is as follows:
type CustomerName(firstName : string, middleName, lastName) = member __.FirstName = firstName member __.MiddleName = middleName member __.LastName = lastName
In the first row of the previous example, the keyword type suggests that CustomerName
is a class. Unlike C#, you can see that the constructor signature is declared in the same row.
Instead, the keyword member indicates all the class members. In this particular case, members are properties, but they could be also methods.
In the following example, you can observe how the signature of the member
method is declared and one of these this.SetMutable
method allows us to change the mutable value of the private member:
type MyClass(intParam : int, strParam : string) = let mutable mutableValue = 42 member this.SetMutable x = mutableValue <- x member this.CurriedAdd x y = x + y member this.TupleAdd(x,y) = x + y
Like in C# and VB, in these functional languages, derivate and abstract class concepts exist:
type DerivedClass(param1, param2) = inherit BaseClass(param1) [<AbstractClass>] type GeometricBaseClass() = abstract member Add: int -> int -> int // abstract method abstract member Pi : float // abstract immutable property abstract member Area : float with get,set // abstract read/write property
The code to declare interface
is similar to the abstract class signature, as you can see in the following example:
type IGeometricBase = abstract member Add: int -> int -> int // abstract method abstract member Pi : float // abstract immutable property abstract member Area : float with get,set // abstract read/write property
The main syntax difference between the abstract
class and interface
is the signature. In the first one, the parenthesis must be written straight after the name, contrary to the other one.
In the .NET Framework, when it comes to collections, the first thing that comes to mind is the Language Integrated Query (LINQ). It is a revolutionary component, included for the first time in .NET version 3.5, which provides a set of instructions and keywords to query objects, in particular, collections.
It is easy to see at this point how important the collections and all that concerns them are.
In F#, there are three major types of collection: array
, list
, and n
. Each of them exhibits a set of functions and properties to elaborate the collection themselves.
Array
, by definition, is fixed-size, zero-based, mutable collection of consecutive data elements that are all of the same type:
let myArr1 = [| 'a'; 'b'; 'c' |]
The myArr1
variable identifies an array of chars. The keywords [| |]
are used to declare array
and, in this case, the values are entered by separating them with a semicolon(;
).
There are many other ways to create array
, and array
of different dimension exist. Some of them are shown in the following examples:
let myArr2 = [| 1 .. 5 |] let myArr3 = [| for i in 1 .. 5 -> i * i |] let arrayOfZeroes : int array = Array.zeroCreate 5 let multiArr = [| [|0,0|]; [|1,1|] |]
let arr = [|0..4..16|] //val arr : int [] = [|0; 2; 4; 6; 8; 10; 12; 14; 16|]
let a = myArr2.[1] // 2 let b = myArr2.[2..] // 3,4,5 let c = myArr2.[..2] // 1,2,3 let d = multiArr.[1] // [|(1, 1)|] myArr2.[1 ] <- 3 // to change a value
List, in F#, is an ordered and immutable series of elements of the same type. The syntax is not very different other than the keywords used for declaration; list
uses [ ]
and array uses [| |]
:
let myList2 = [ 1 .. 5 ] let myList3 = [ for i in 1 .. 5 -> i * i ] let list = [0..2..16] let a = myList2.[1] // 2 let b = myList2.[2..] // 3,4,5 let c = myList2.[..2] // 1,2,3
A sequence can be easily confused with list
because both the collections are used to represent an ordered and huge series of elements, all of the same type. However, sequences, unlike lists, are useful if you do not want to use all the elements of the collection.
Elements can represent numerous data structures and can be managed in many different ways, for example, through some operations, such as grouping, counting, and extracting functions.
In F#, sequences are defined with the syntax seq<T>
also known as IEnumerable<T>
, as you can see in the following example:
let myseq1 = seq { 0 .. 10 .. 100 } let myseq2 = seq { for i in 1 .. 5 do yield i * i } let myList3 = Seq.init 5 (fun n -> n * 5)
The real power of arrays, lists, and sequences is their ability to manipulate data using their methods and the possibility to concatenate methods through the pipe operator or others.
In the following example, you can notice how simple it is to transform data and type collection only with a few instructions using the type array, sequence, and list. In the end, values are printed:
[| 1 .. 100 |] |> Seq.ofArray |> Seq.map (fun x -> x * x) |> List.ofSeq |> List.iter (fun y -> printfn "the value is: %i " y)
The code also suggests how essential it is to know collections and their methods. As you can see in the following table, there are so many methods and overloads of them that could be useful having on hand the table with all the possibilities (refer to complete table MSDN link https://msdn.microsoft.com/en-us/library/hh967652.aspx) . Take a look at the following table:
Function |
Array |
List |
Seq |
Description |
|
● |
● |
● |
Returns a new collection that contains the elements of the first collection, followed by elements of the second collection. |
|
- |
- |
- |
Returns a new collection with the element added. |
|
● |
● |
● |
Returns the average of the elements in the collection. |
|
● |
● |
● |
Returns the average of the results of the provided function applied to each element. |
|
● |
- |
- |
Copies a section of an array. |
|
- |
- |
● |
Computes and stores elements of a sequence. |
|
- |
- |
● |
Converts the elements to the specified type. |
|
● |
● |
● |
Applies the given function |
|
● |
● |
● |
Applies the given function to each element of the collection, concatenates all the results, and returns the combined list. |
|
- |
- |
● |
Compares two sequences by using the given comparison function element by element. |
|
● |
● |
● |
Combines the given enumeration-of-enumerations as a single concatenated enumeration. |
|
- |
- |
- |
Returns true if the set contains the specified element. |
|
- |
- |
- |
Tests whether an element is in the domain of a map. |
|
- |
- |
- |
Returns the number of elements in the set. |
|
- |
- |
● |
Applies a key generating function to each element of a sequence, and returns a sequence that yields unique keys and their number of occurrences in the original sequence. |
|
● |
- |
● |
Copies the collection. |
|
● |
- |
- |
Creates an array of whole elements that are all initially the given value. |
|
- |
- |
● |
Returns a sequence that's built from the given delayed specification of a sequence. |
|
- |
- |
- |
Returns a new set with the elements of the second set removed from the first set. |
|
● |
Returns a sequence that contains no duplicate entries according to generic hash and equality comparisons on the entries. If an element occurs multiple times in the sequence, later occurrences are discarded. | ||
|
● |
Returns a sequence that contains no duplicate entries according to the generic hash and equality comparisons on the keys that the given key generating function returns. If an element occurs multiple times in the sequence, later occurrences are discarded. | ||
|
● |
● |
● |
Creates an empty collection. |
|
● |
● |
● |
Tests whether any element of the sequence satisfies the given predicate. |
|
● |
- |
● |
Tests whether any pair of corresponding elements of the input sequences satisfies the given predicate. |
|
● |
Sets a range of elements of the array to the given value. | ||
|
● |
● |
● |
Returns a new collection that contains only the elements of the collection for which the given predicate returns |
|
● |
● |
● |
Returns the first element for which the given function returns true. Returns |
|
● |
● |
● |
Returns the index of the first element in the array that satisfies the given predicate. Raises |
|
- |
- |
- |
Evaluates the function on each mapping in the collection, and returns the key for the first mapping where the function returns true. If no such element exists, this function raises |
|
● |
● |
● |
Applies a function to each element of the collection, threading an accumulator argument through the computation. If the input function is |
|
● |
● |
- |
Applies a function to the corresponding elements of two collections, threading an accumulator argument through the computation. The collections must have identical sizes. If the input function is |
|
● |
● |
- |
Applies a function to each element of the collection, threading an accumulator argument through the computation. If the input function is |
|
● |
● |
- |
Applies a function to the corresponding elements of two collections, threading an accumulator argument through the computation. The collections must have identical sizes. If the input function is f and the elements are |
|
● |
● |
● |
Tests whether all the elements of the collection satisfy the given predicate. |
|
● |
● |
● |
Tests whether all the corresponding elements of the collection satisfy the given predicate pairwise. |
|
● |
● |
● |
Returns an element from the collection given its index. |
|
- |
● |
● |
Returns the first element of the collection. |
|
● |
● |
● |
Creates a collection given the dimension and a |
|
- |
- |
● |
Generates a sequence that, when iterated, returns successive elements by calling the given function. |
|
- |
- |
- |
Computes the intersection of two sets. |
|
- |
- |
- |
Computes the intersection of a sequence of sets. The sequence must not be empty. |
|
● |
● |
● |
Returns |
|
- |
- |
- |
Returns |
|
- |
- |
- |
Returns |
|
- |
- |
- |
Returns |
|
- |
- |
- |
Returns |
|
● |
● |
● |
Applies the given function to each element of the collection. |
|
● |
● |
● |
Applies the given function to each element of the collection. The integer that's passed to the function indicates the index of the element. |
|
● |
● |
- |
Applies the given function to a pair of elements that are drawn from matching indices in two arrays. The integer that's passed to the function indicates the index of the elements. The two arrays must have the same length. |
|
● |
● |
● |
Applies the given function to a pair of elements that are drawn from matching indices in two arrays. The two arrays must have the same length. |
|
● |
● |
● |
Returns the number of elements in the collection. |
|
● |
● |
● |
Builds a collection whose elements are the results of applying the given function to each element of the array. |
|
● |
● |
● |
Builds a collection whose elements are the results of applying the given function to the corresponding elements of the two collections pairwise. The two input arrays must have the same length. |
|
- |
● |
- |
Builds a collection whose elements are the results of applying the given function to the corresponding elements of the three collections simultaneously. |
|
● |
● |
● |
Builds an array whose elements are the results of applying the given function to each element of the array. The integer index that's passed to the function indicates the index of the element that's being transformed. |
|
● |
● |
- |
Builds a collection whose elements are the results of applying the given function to the corresponding elements of the two collections pairwise, also passing the index of the elements. The two input arrays must have the same length. |
|
● |
● |
● |
Returns the greatest element in the collection, compared by using the |
|
● |
● |
● |
Returns the greatest element in the collection, compared by using |
|
- |
- |
- |
Returns the greatest element in the set according to the ordering that's used for the set. |
|
● |
● |
● |
Returns the least element in the collection, compared by using the |
|
● |
● |
● |
Returns the least element in the collection, compared by using the |
|
- |
- |
- |
Returns the lowest element in the set according to the ordering that's used for the set. |
|
- |
● |
● |
Creates |
|
● |
- |
● |
Creates |
|
● |
● |
- |
Creates |
|
- |
- |
● |
Returns a sequence of each element in the input sequence and its predecessor except for the first element, which is returned only as the predecessor of the second element. |
|
● |
● |
- |
Splits the collection into two collections. The first collection contains the elements for which the given predicate returns |
|
● |
● |
- |
Returns an array with all the elements permuted according to the specified permutation. |
|
● |
● |
● |
Applies the given function to successive elements, returning the first result where the function returns |
|
- |
- |
● |
Creates a sequence object that delegates to the given sequence object. This operation ensures that a type cast can't rediscover and mutate the original sequence. For example, if given an array, the returned sequence will return the elements of the array, but you can't cast the returned sequence object to an array. |
|
● |
● |
● |
Applies a function to each element of the collection, threading an accumulator argument through the computation. This function starts by applying the function to the first two elements, passes this result into the function along with the third element, and so on. The function returns the final result. |
|
● |
● |
- |
Applies a function to each element of the collection, threading an accumulator argument through the computation. If the input function is |
|
- |
- |
- |
Removes an element from the domain of the map. No exception is raised if the element isn't present. |
|
- |
● |
- |
Creates a list of a specified length with every element set to the given value. |
|
● |
● |
- |
Returns a new list with the elements in reverse order. |
|
● |
● |
● |
Applies a function to each element of the collection, threading an |
|
● |
● |
- |
Resembles the |
|
- |
- |
● |
Returns a sequence that yields only one item. |
|
● |
- |
- |
Sets an element of an array to the specified value. |
|
- |
- |
● |
Returns a sequence that skips |
|
- |
- |
● |
Returns a sequence that, when iterated, skips elements of the underlying sequence while the given predicate returns true and then yields the remaining elements of the sequence. |
|
● |
● |
● |
Sorts the collection by element value. Elements are compared using compare. |
|
● |
● |
● |
Sorts the given list by using keys that the given projection provides. Keys are compared using compare. |
|
● |
- |
- |
Sorts the elements of an array by mutating it in place and using the given comparison function. Elements are compared by using |
|
● |
- |
- |
Sorts the elements of an array by mutating it in place and using the given projection for the keys. Elements are compared by using |
|
● |
- |
- |
Sorts the elements of an array by mutating it in place and using the given |
|
● |
● |
- |
Sorts the elements of |
|
● |
- |
- |
Builds an array that contains the given subrange that's specified by starting |
|
● |
● |
● |
Returns the sum of the elements in the collection. |
|
● |
● |
● |
Returns the sum of the results that are generated by applying the function to each element of the collection. |
|
- |
● |
- |
Returns the list without its first element. |
|
- |
- |
● |
Returns the elements of the sequence up to a specified count. |
|
- |
- |
● |
Returns a sequence that, when iterated, yields the elements of the underlying sequence while the given predicate returns true and then returns no more elements. |
|
- |
● |
● |
Creates an array from the given |
|
● |
- |
● |
Creates a list from the given |
|
● |
● |
- |
Creates a sequence from the given |
|
- |
- |
● |
Returns a sequence that, when enumerated, returns no more than N elements. |
|
● |
● |
● |
Searches for an element that satisfies a given predicate. |
|
● |
● |
● |
Searches for the first element that satisfies a given predicate and returns the index of the matching element or |
|
- |
- |
- |
Returns the key of the first mapping in the collection that satisfies the given predicate or returns |
|
● |
● |
● |
Applies the given function to successive elements, returning the first result where the function returns something for some value. If no such element exists, the operation will return |
|
- |
- |
● |
Returns a sequence that contains the elements that the given computation generates. |
|
- |
- |
- |
Computes the union of the two sets. |
|
- |
- |
- |
Computes the union of a sequence of sets. |
|
● |
● |
● |
Splits a list of pairs into two lists. |
|
● |
● |
● |
Splits a list of triples into three lists. |
|
- |
- |
● |
Returns a sequence that yields the sliding windows of containing elements that are drawn from the input sequence. Each window is returned as a fresh array. |
|
● |
● |
● |
Combines the two collections into a list of pairs. The two lists must have equal lengths. |
|
● |
● |
● |
Combines the three collections into a list of triples. The lists must have equal lengths. |
In addition, in the next paragraph, you will see how to apply a case condition for each possibility of interrogation, manipulation, or extrapolation data.
3.145.18.101