Book Image

.NET Core 2.0 By Example

By : Neha Shrivastava, Rishabh Verma
Book Image

.NET Core 2.0 By Example

By: Neha Shrivastava, Rishabh Verma

Overview of this book

With the rise in the number of tools and technologies available today, developers and architects are always exploring ways to create better and smarter solutions. Before, the differences between target platforms was a major roadblock, but that's not the case now. .NET Core 2.0 By Example will take you on an exciting journey to building better software. This book provides fresh and relevant content to .NET Core 2.0 in a succinct format that’s enjoyable to read. It also delivers concepts, along with the implications, design decisions, and potential pitfalls you might face when targeting Linux and Windows systems, in a logical and simple way. With the .NET framework at its center, the book comprises of five varied projects: a multiplayer Tic-tac-toe game; a real-time chat application, Let'sChat; a chatbot; a microservice-based buying-selling application; and a movie booking application. You will start each chapter with a high-level overview of the content, followed by the above example applications described in detail. By the end of each chapter, you will not only be proficient with the concepts, but you’ll also have created a tangible component in the application. By the end of the book, you will have built five solid projects using all the tools and support provided by the .NET Core 2.0 framework.
Table of Contents (16 chapters)
Title Page
Packt Upsell
Contributors
Preface
Index

F# primer


F# is a functional programming language. Functional programming treats programs as mathematical expressions. It focuses on functions and constants that don't change, rather than variables and states. F# is a Microsoft programming language for concise and declarative syntax. Let's begin with a brief history of how this language came into existence. The first attempt at functional programming was Haskell .NET. F# development began in 2005 and after that, various versions came along. At the time of writing this chapter, F# 4.1 is the latest version; it was released in March 2017. This comes with Visual Studio 2017 and supports .NET Core.

The F# language can be used for the following tasks:

  • To solve mathematical problems
  • For graphic design
  • For financial modeling
  • For compiler programming
  • For CPU design

It is also used for CRUD applications, web pages GUI games, and other programs.

F# keywords

Keywords and their use in the F# language are outlined in the following table:

Keyword

Description

abstract

Indicates that either it has no implementation or is a virtual and has default implementation.

begin

In verbose syntax, indicates the start of a code block.

default

Indicates an implementation of an abstract method; used together with an abstract method declaration to create a virtual method.

elif

Used in conditional branching. A short form of else-if.

end

Used in type definitions and type extensions, indicates the end of a section of member definitions.

In verbose syntax, used to specify the end of a code block that starts with the begin keyword.

exception

Used to declare an exception type.

finally

Used together with try to introduce a block of code that executes regardless of whether an exception occurs.

fun

Used in lambda expressions, and is also known as anonymous functions.

function

Used as a shorter alternative to the fun keyword and a match expression in a lambda expression that has pattern matching on a single argument.

inherit

Used to specify a base class or base interface.

interface

Used to declare and implement interfaces.

let

Used to associate or bind a name to a value or function.

member

Used to declare a property or method in an object type.

mutable

Used to declare a variable; that is, a value that can be changed.

override

Used to implement a version of an abstract or virtual method that differs from the base version.

rec

Used to indicate that a function is recursive.

select

Used in query expressions to specify what fields or columns to extract. Note that this is a contextual keyword, which means that it is not actually a reserved word and it only acts like a keyword in an appropriate context.

static

Used to indicate a method or property that can be called without an instance of a type, or a value member that is shared among all instances of a type.

struct

Used to declare a structure type. Also used in generic parameter constraints. Used for OCaml compatibility in module definitions.

type

Used to declare a class, record, structure, discriminated union, enumeration type, unit of measure, or type abbreviation.

val

Used in a signature to indicate a value, or in a type to declare a member, in limited situations.

yield

Used in a sequence expression to produce a value for a sequence.

 

This reference is taken from the Microsoft official website, and a detailed explanation and description can be found at https://docs.microsoft.com/en-us/dotnet/fsharp/language-reference/keyword-reference.

Comments

In the F# language, we have two types of comments for a single line and for multiple lines. This is the same as C#. The following are the two types of comments:

  • A single-line comment which starts with the // symbol.

               Example: // returns an integer exit code

  • A multi-line comment which starts with (* and ends with *).

               Example:  (*Learn more about F# at http://fsharp.org *)

Data types

F# has a rich data type system. We can broadly classify them as:

  • Integral typessbyte, byte, int16, uint16, int32, uint32, int64, and bigint
  • Floating point typesfloat32, float, and decimal
  • Text typeschar and string 
  • Other typesbool

These types are also referred to as fundamental primitive types in F#. Apart from these, F# has an exhaustive list of predefined types as well, such as lists, arrays, records, enumerations, tuples,  units, sequences, and so on. It is recommended that a person learning F# goes through the official Microsoft documentation on F# at https://docs.microsoft.com/en-us/dotnet/fsharp/.

Variable declaration

F# uses the let keyword for the declaration of a variable, for example:

let square x = x*x

The compiler automatically detects this as a value type. If we pass a float value, the compiler will be able to understand that without declaring a data type. Variables in F# are immutable, so once a value is assigned to a variable, it can't be changed. They are compiled as static read-only properties.

The following example demonstrates this:

let x:int32 = 50
let y:int32 = 30
let z:int32 = x + y

Variables x, y, and z are all of type int32 and are immutable, meaning their value cannot be changed.

Let's print their values. The syntax is as follows:

printfn "x: %i" x
printfn "y: %i" y
printfn "z: %i" z

After the preceding code executes, the result is as follows:

x: 50
y: 30
z: 80

Now, suppose we want to modify the value of x from 50 to 60 and check that z reflects the updated sum; we will write the code as:

let x = 60
let y = 30
let z = x + y

On executing this code, we get the following errors, and rightly so because x and z are immutable:

Duplicate definition of value 'x'
Duplicate definition of value 'z'

The correct way of doing it would be to use mutable variables for the declaration, as shown here:

let mutable x = 60
let y = 30 //It's optional to make y mutable.
let mutable z = x + y
x <- 70
z <-  x + y

On printing the values again, we will see:

x: 70
y: 30
z: 100

Operators 

F# has the following operators:

  • Arithmetic operators
  • Comparison operators
  • Boolean operators
  • Bitwise operators

Let's discuss these operators in detail.

Arithmetic operators

Arithmetic operators supported by the F# language are outlined in the following table. Assuming variable X = 10 and variable Y = 40 , we have the following expressions:

Operator

Description

Example

+

Adds two values

X + Y  = 50

-

Subtracts the second value from the first

X - Y = -30

*

Multiplies both values

X * Y = 400

/

Divides two values

Y / X = 4

%

Modulus operator and gives the value of the remainder after an integer division

Y % X = 0

**

Exponentiation operator; raises one variable to the power of another

Y**X = 4010

Comparison operators

The following table shows all the comparison operators supported by F# . These operators return true or false.

Let's take X = 20 and Y = 30:

Operator

Description

Example

==

Verifies the values of two variables are equal; if not, then the condition becomes false.

(X == Y) returns false

<> 

Verifies the values of two variables are equal; if values are not equal then the condition becomes true.

(X <> Y) returns true

> 

Verifies the value of the left variable is greater than the value of the right variable; if not, then the condition becomes false.

(X > Y) returns false

< 

Verifies the value of the left variable is less than the value of the right variable; if yes, then the condition becomes true.

(X < Y) returns true

>=

Verifies the value of the left variable is greater than or equal to the value of the right variable; if not, then condition becomes false.

(X >= Y) returns false

<=

Verifies the value of the left variable is less than or equal to the value of the right variable; if yes, then the condition becomes true.

(X <= Y) returns true

Boolean operators

The following table shows all the Boolean operators supported by the F# language. Let's take variable X as true and Y as false:

Operator

Description

Example

&&

Boolean AND operator. If both the bool values are true means 1, then the condition is true.

(X && Y) is false

||

Boolean OR operator. If either of the two bool values is true means 1, then the condition is true.

(X || Y) is true

not

Boolean NOT operator. If the condition is true, then the logical NOT operator will become false and vice versa.

not (X && Y) is true

Bitwise operators

Bitwise operators work on bits and perform bit-by-bit operations. The truth tables for &&& (bitwise AND), ||| (bitwise OR), and ^^^ (bitwise exclusive OR) are shown as follows. In the following table, the first variable is X and the second variable is Y:

X

Y

X &&& Y

X ||| Y

X ^^^ Y

0

0

0

0

0

0

1

0

1

1

1

1

1

1

0

1

0

0

1

1

 

It also supports ~~~(Unary, effect of flipping bits) , <<< (left shift operator), and >>>(right shift operator).

Decision-making statements

The F# language has the following (if...else and loop) types of decision-making statements.

if statements

The following table shows all the ways of implementing if statements:

Statement

Description

if/then statement

 An if/then statement consists of a Boolean expression followed by one or more statements.

if/then/else statement

An if/then statement can be followed by an optional else statement, which executes when the Boolean expression is false.

if/then/elif/else statement

An if/then/elif/else statement allows you to have multiple else statements.

Nested if statements

You can use one if or else if statement inside another if or else if statements.

Loop statements

F# provides the following types of loop:

Loop type

Description

for…to and for…downto expressions

The for...to expression is used to iterate in a loop over a range of values of a loop variable. The for…downto expression reduces the value of a loop variable.

for…in expression

This form of for loop is used to iterate over collections of items; that is, loops over collections and sequences.

while…do loop

Repeats a statement or group of statements while the given condition is true. It tests the condition before executing the loop body.

nested loops

We can use one or more loop inside any other for or while loop.

F# functions

F# functions act like variables. We can declare and use them in the same way as we use variables in C#. A function definition starts with the let keyword, followed by the function name and parameters, a colon, its type, and the right-side expression, showing what the function does. The syntax is follows:

Let functionName parameters [ : returnType] = functionbody

In the preceding syntax:

  • functionName is an identifier of the function.
  • parameters gives the list of parameters separated by spaces. We can also specify an explicit type for each parameter and if not specified, the compiler tends to presume it from the function body as variables.
  • functionbody comprises an expression, or a compound expression, which has number of expressions. The final expression in the function body is the return value.
  • returnType is a colon followed by a type and it is optional. If the returnType is not specified, then the compiler determines it from the final expression in the function body.

Have a look at the following example for our syntax: 

let addValue (x : int) = 5 + x

Calling a function

A function can be called by passing the function name followed, by a space, and then arguments (if any) separated by spaces, as shown here:

let sum = addValue 3

We can perform many tasks using F# functions, some of which are as follows:

  • We can create a new function and link that function with a type as it acts as a variable type:let square x = x*x
  • We can perform some calculations as well, such as:let square x = x*x
  • We can assign a value. Taking the same example:let square x = x*x
  • We can pass a function as a parameter to another function like this:let squareValue = List.map square[1;2;3] // using square function
  • We can return a function as a result of another function example:let squareValue = List.map square[1;2;3]
File sequence

The order of files in a project matters in an F# solution. The file used in any function should be placed above the file where the function is used, because F# has a forward-only model of compilation.

Unlike C#, where the file sequence doesn't matter, the sequencing of files does matter in F#. For example, consider that Program.fs is using DotNetCorePrint.fs. So, DotNetCorePrint.fs should be placed above Program.fs in the solution; otherwise, it will throw a compilation error. To move a file up or down, we can right-click on the file and select Move Up or the keys Alt + the up arrow to move the file. The ordering of the files in Solution Explorer can be seen in the following screenshot:

Basic input/output syntax

We are now going to see how to write and read in F#. To read and write into the console, we can use the following commands:

  • To write: System.Console.Write("Welcome!")
  • To read: System.Console.Read()
  • To print: printfn "Hello"

Let's compare F# with C#:

                                F#

C#

The F# user is free from the obligation of defining types; for example: let square x = x* x

The compiler can identify (integer * integer) or (float * float) when we pass a value.

 

The C# user is bound to provide a type:

Public int square(int x){ return x = x*x ; }

 

F# has immutable data, and the value never changes; for example: let number = [3;2;1]

let moreNumber = 4:: number

In the preceding example, to add one more number, (4), we need to create a new list of items using number, and add the new record, 4 in this case. The same list doesn't get modified for safer asynchronous execution and a simplified understanding of a function.

C# has mutable as well as immutable data. Strings are immutable, the rest are all mutable, for example:

var number = new List<int> {1,2,3}; number.Add(4);

In the preceding example, we created a list and added a new item to the same list. The list gets modified.

F# compiles code in an order. It is favoured for data processing and algorithmic computation. It doesn't work on visibility; it works sequentially.

 

C# code works on visibility. The sequence doesn't matter.

The order of files in a project matters in the F# solution. A file used in any method should be placed above the file where the method has been used.

 

The order doesn't matter in C#.

 

 

 

 

F# has precise syntax; it focuses on what not and how; for example:

let square x = x*x

let squared = List.map square[1;2;3]  // using square function

Right-click execute | result1;4;9

F# uses declarative syntax, not imperative syntax as in C#. F# helps us to minimize accidental complexity, meaning complexity that is not a part of the problem, but which we introduced as part of the solution, for example:

let rec quicksort = function | [] -> [] | x :: xs ->  let smaller = List.filter((>)x) xs let larger = List.filter ((<=)x) xs

Quicksort smaller @ [x] @ quicksort larger:

 

Let sorted = quicksort [4;5;4;7;9;1;6;1;0;-99;10000;3;2]

 

rec is used for a recursive function. It is assigned in the sorted result. Run it and you will get a sorted result. It is a very simple sorting function with less complexity than C#, which has a high chance of errors.

 

C# code implementation is more about how to implement. It sometimes increases unnecessary complexity as part of the solution to a problem.

Quick sort has a very complex algorithm and a high chance of increasing accidental complexity.