Google News
logo
F# Interview Questions
1 .
* F# is a universal programming language for writing succinct, robust and performant code.
 
* F# allows you to write uncluttered, self-documenting code, where your focus remains on your problem domain, rather than the details of programming.
 
* It does this without compromising on speed and compatibility - it is open-source, cross-platform and interoperable.
open System // Gets access to functionality in System namespace.

// Defines a list of names
let names = [ "Peter"; "Julia"; "Xi" ]

// Defines a function that takes a name and produces a greeting.
let getGreeting name = $"Hello, {name}"

// Prints a greeting for each name!
names
|> List.map getGreeting
|> List.iter (fun greeting -> printfn $"{greeting}! Enjoy your F#")
F# has many features. Following are the main features of F# :
 
* Type inference
* Type extension
* Less code
* Immutable data
* Pattern matching
* Assertion
* Lambda expression
* Function composition and pipelining
* Object expression
* Lazy computation and many more..
* Lightweight syntax
* Immutable by default
* Type inference and automatic generalization
* First-class functions
* Powerful data types
* Pattern matching
* Async programming
* If you're downloading Visual Studio for the first time, it will first install Visual Studio Installer. Install the appropriate edition of Visual Studio from the installer.
 
* If you already have Visual Studio installed, choose Modify next to the edition you want to add F# to.
 
* On the Workloads page, select the ASP.NET and web development workload, which includes F# and .NET Core support for ASP.NET Core projects.
 
* Choose Modify in the lower right-hand corner to install everything you've selected.
 
* You can then open Visual Studio with F# by choosing Launch in Visual Studio Installer.
The following table shows reference articles that describe literals and strings in F#.

Title Description
Literals Learn about the syntax for literal values in F# and how to specify type information for F# literals.
Strings Learn about strings in F#. The string type represents immutable text, as a sequence of Unicode characters. string is an alias for System.String in .NET.
Interpolated strings Learn about interpolated strings, a special form of string that allows you to embed F# expressions directly inside them.
F# provides a rich set of data types. It helps to deal with any data whether it is scientific data, data of business analysis, etc. You can see the table of data types here.
 
Data Types in F#

Types Data Types
Primitive data types char, byte, bool, int, float
Derived data types class, array, list, records, sequence
Enumeration enum
Unit type It is used if other data types are not specified.
Exception handling is the standard way of handling error conditions in the .NET Framework. Thus, any .NET language must support this mechanism, including F#. An exception is an object that encapsulates information about an error. When errors occur, exceptions are raised and regular execution stops. Instead, the runtime searches for an appropriate handler for the exception. The search starts in the current function, and proceeds up the stack through the layers of callers until a matching handler is found. Then the handler is executed.
 
In addition, as the stack is unwound, the runtime executes any code in finally blocks, to guarantee that objects are cleaned up correctly during the unwinding process.
 
Example :
let ExExample a b =  
 let mutable c = 0  
 c <- (a/b)  
 printfn "Rest of the code"  
  
ExExample 10 0 
The following table shows reference articles that describe language concepts related to exception handling.

Title Description
Exception Handling Contains information about exception handling support in F#.
The try...with Expression Learn about how to use the try...with expression for exception handling.
The try...finally Expression Learn about how the F# try...finally expression enables you to execute clean-up code even if a block of code throws an exception.
The use Keyword Learn about the keywords use and using, which can control the initialization and release of resources.
Assertions Learn about the assert expression, which is a debugging feature that you can use to test an expression. Upon failure in Debug mode, an assertion generates a system error dialog box.
In F#, you can create a user-defined exception. It provides flexibility to define custom exceptions according to requirement.
 
Example :
let ExExample a b =  
  let mutable c = 0  
  try  
    c <- (a/b)  
  with  
    | :? System.DivideByZeroException as e -> printfn "%s" e.Message  
  printfn "Rest of the code"  
  
ExExample 10 0  
In F#, you can throw exceptions explicitly. You are allowed to throw a custom exception. You can also throw exceptions by using predefined methods of Exception like Failwith and InvalidArgs.
 
Example of FailWith keyword : 
let TestAgeValidation age  =  
  try  
     if (age<18) then failwith "Sorry, Age must be greater than 18"  
  with  
     | Failure(e) -> printfn "%s" e;   
  printf "Rest of the code"  
TestAgeValidation 10  
Example of InvalidArg keyword
let TestInvalidArgument age =  
 if(age<18) then  
  invalidArg "TestInvalidArgument" ("Sorry, Age must be greater than 18")  
  
TestInvalidArgument 10
The assert expression is a debugging feature of F#. You can use it to test an expression. It generates a system error dialog box upon failure in Debug mode.
 
Example : 
let divide (x:int, y:int):int =  
 assert (x>0)  
 let z = (x/y)  
 z  
  
let result = divide(10,2)  
printf "%d" result  
The Module is a collection of classes, functions, and types. It helps to organize related code so we can maintain code easily.
 
Example :
open Arithmetic  
printf "%d" (sub 20 10)
Access control specifies the accessibility of code. By using these, you can specify the scope of data, method, class, etc.
 
There are 3 types of access control in F#.
 
Public : Public access control also known as default. It is accessible for all. If you don't specify any access control explicitly in your code, by default it follows public access control.

Private : F# provides private keyword to declare private members in the class or type. The scope of private is limited to the local block in which it is declared.

Internal : Internal access control is only accessible from the same assembly. An assembly is a file which is automatically generated by your compiler after compilation of F# code.
The string type represents immutable text as a sequence of Unicode characters. string is an alias for System.String in .NET.

Remarks : String literals are delimited by the quotation mark (") character. The backslash character ( \ ) is used to encode certain special characters. The backslash and the next character together are known as an escape sequence. Escape sequences supported in F# string literals are shown in the following table.


Character Escape sequence
Alert \a
Backspace \b
Form feed \f
Newline \n
Carriage return \r
Tab \t
Vertical tab \v
Backslash \\
Quotation mark \"
Apostrophe \'
Unicode character \DDD (where D indicates a decimal digit; range of 000 - 255; for example, \231 = "ç")
Unicode character \xHH (where H indicates a hexadecimal digit; range of 00 - FF; for example, \xE7 = "ç")
Unicode character \uHHHH (UTF-16) (where H indicates a hexadecimal digit; range of 0000 - FFFF; for example, \u00E7 = "ç")
Unicode character \U00HHHHHH (UTF-32) (where H indicates a hexadecimal digit; range of 000000 - 10FFFF; for example, \U0001F47D = "👽")
If preceded by the @ symbol, the literal is a verbatim string. Declaring a verbatim string means that any escape sequences are ignored, except that two quotation mark characters are interpreted as one quotation mark character.
Additionally, a string may be enclosed by triple quotes. In this case, all escape sequences are ignored, including double quotation mark characters. To specify a string that contains an embedded quoted string, you can either use a verbatim string or a triple-quoted string. If you use a verbatim string, you must specify two quotation mark characters to indicate a single quotation mark character. If you use a triple-quoted string, you can use the single quotation mark characters without them being parsed as the end of the string. This technique can be useful when you work with XML or other structures that include embedded quotation marks.
// Using a verbatim string
let xmlFragment1 = @"<book author=""Milton, John"" title=""Paradise Lost"">"

// Using a triple-quoted string
let xmlFragment2 = """<book author="Milton, John" title="Paradise Lost">"""

In code, strings that have line breaks are accepted and the line breaks are interpreted literally as newlines, unless a backslash character is the last character before the line break. Leading white space on the next line is ignored when the backslash character is used. The following code produces a string str1 that has value "abc\ndef" and a string str2 that has value "abcdef".
let str1 = "abc
def"
let str2 = "abc\
def"
You can access individual characters in a string by using array-like syntax. The following examples use [] to index strings. This syntax was introduced in F# 6.0. You can also use .[] to index strings in all versions. The new syntax is preferred.
printfn "%c" str1[1]

The output is : b.
Or you can extract substrings by using array slice syntax, as shown in the following code.
printfn "%s" str1[0..2]
printfn "%s" str2[3..5]
The output is as follows :
abc
def
 
You can represent ASCII strings by arrays of unsigned bytes, type byte[]. You add the suffix B to a string literal to indicate that it's an ASCII string. ASCII string literals used with byte arrays support the same escape sequences as Unicode strings, except for the Unicode escape sequences.
// "abc" interpreted as a Unicode string.
let str1 : string = "abc"
// "abc" interpreted as an ASCII byte array.
let bytearray : byte[] = "abc"B
The unit type is a type which indicates the absence of specific value. The unit type has only a single value. This value acts as a placeholder when no other value exist.
 
Example :
let function1 x y = x + y                 
  
function1 10 20      // this line results a compiler warning  
      
// changing the code to the following and eliminates the warning  
let result = function1 10 20  
  
// Use this if you are calling the function and don't want the return value  
function1 10 20 |> ignore 
Casting is a process of converting one type to another type. F# provides mainly two operators to deal with upcasting and downcasting. The :> operator is used to upcast object and :?> operator is used to downcast object.
 
Example :
type BaseClass() =  
 class  
  member this.ShowClassName()=  
    printfn "BaseClass"  
 end  
  
type DerivedClass() =   
 class  
  inherit BaseClass()  
  member this.ShowClassName()=  
   printfn "DerivedClass"  
 end  
  
let baseClass = new BaseClass()              
let derivedClass : DerivedClass = new DerivedClass()  
baseClass.ShowClassName()      
derivedClass.ShowClassName()  
let castIntoBaseClass = derivedClass :> BaseClass        // upcasting   
castIntoBaseClass.ShowClassName()  
let castIntoDerivedClass = baseClass :?> DerivedClass   // downcasting  
castIntoDerivedClass.ShowClassName()
An operator is simply a symbol that is used to perform operations. There can be many types of operations like arithmetic, bitwise, logical, etc.
 
There are following types of operators to perform different types of operations in F# language.
 
Arithmetic operators : The Arithmetic operators take numerical values as their operands and return a single numerical value.

Boolean operators : The Boolean operators are used to check conditional expressions. It returns true if expression satisfies the condition otherwise it returns false.

Bitwise operators : In F#, the bitwise operator works on individual bits and return a result after evaluation.

Nullable operators : The Nullable operators are used to work with database queries. It handles null values which are stored in tables in place of data.
Keywords : It contains the links to information about all F# language keywords.

Symbol and operators : It contains a table of symbols and operators that are used in the F# language.
A binding associates an identifier with a value or function. You use the let keyword to bind a name to a value or function.
// Binding a value:
let identifier-or-pattern [: type] =expressionbody-expression
// Binding a function value:
let identifier parameter-list [: return-type ] =expressionbody-expression
Functions are the fundamental unit of program execution in any programming language. As in other languages, an F# function has a name, can have parameters and take arguments, and has a body. F# also supports functional programming constructs such as treating functions as values, using unnamed functions in expressions, composition of functions to form new functions, curried functions, and the implicit definition of functions by way of the partial application of function arguments.
 
You define functions by using the let keyword, or, if the function is recursive, the let rec keyword combination.
 
Syntax :
// Non-recursive function definition.
let [inline] function-name parameter-list [ : return-type ] = function-body
// Recursive function definition.
let rec function-name parameter-list = recursive-function-body
In F#, functions can be composed from other functions. It is a process of composing in which a function represents the application of two composed functions. F# function pipelining allows us to call functions in the chain. Pipelining operator takes a function and an argument as operands and returns a value.
 
F# Function Composition Example :
let function1 name=   
  name + " FSharp"  
let function2 name =   
   name + " Programming"  
  
let programmingName = function1 >> function2  
let result = programmingName "Hello"  
printf "%s" result  
 
F# Function Pipelining Example :
let function1 name=   
  name + " FSharp"  
let function2 name =   
   name + " Programming"  
  
let result = "Hello" |> function1 |> function2  
printf "%s" result  
Lambda expression is an unnamed or anonymous function. Sometimes instead of defining a full name function, you may create a lambda expression. It optimizes the code. You must use a fun keyword to define lambda expression.
 
Example :
let result = (fun a -> a+1) 100  
printf "%d" result  
The F# inline function is a function that is integrated directly into the calling code. It helps to optimize code and sometimes can improve performance too.
 
Example :
type InlineClass() =  
  class  
    member inline this.inlineFunction(a) = a*a  
  end  
   
let obj = new InlineClass()  
let result = obj.inlineFunction 2  
printf "%d" result
The rec keyword is used together with the let keyword to define a recursive function.
 
Syntax :
// Recursive function:
let rec function-nameparameter-list =
    function-body

// Mutually recursive functions:
let rec function1-nameparameter-list =
    function1-body

and function2-nameparameter-list =
    function2-body
...
 
Recursive functions - functions that call themselves - are identified explicitly in the F# language with the rec keyword. The rec keyword makes the name of the let binding available in its body.
 
The following example shows a recursive function that computes the nth Fibonacci number using the mathematical definition.
let rec fib n =
    match n with
    | 0 | 1 -> n
    | n -> fib (n-1) + fib (n-2)

 

Methods are implicitly recursive within the type they are defined in, meaning there is no need to add the rec keyword. For example :

type MyClass() =

    member this.Fib(n) =

        match n with

        | 0 | 1 -> n

        | n -> this.Fib(n-1) + this.Fib(n-2)

 

Let bindings within classes are not implicitly recursive, though. All let-bound functions require the rec keyword.

Patterns are rules for transforming input data. They are used throughout F# to compare data with a logical structure or structures, decompose data into constituent parts, or extract information from data in various ways.
 
Patterns are used in many language constructs, such as the match expression. They are used when you are processing arguments for functions in let bindings, lambda expressions, and in the exception handlers associated with the try...with expression. For more information, see Match Expressions, let Bindings, Lambda Expressions: The fun Keyword, and Exceptions: The try...with Expression.
 
For example, in the match expression, the pattern is what follows the pipe symbol.
match expression with
| pattern [ when condition ] -> result-expression
...
Each pattern acts as a rule for transforming input in some way. In the match expression, each pattern is examined in turn to see if the input data is compatible with the pattern. If a match is found, the result expression is executed. If a match is not found, the next pattern rule is tested. The optional when condition part is explained in Match Expressions.
 
Supported patterns are shown in the following table. At run time, the input is tested against each of the following patterns in the order listed in the table, and patterns are applied recursively, from first to last as they appear in your code, and from left to right for the patterns on each line.

Name Description Example
Constant pattern Any numeric, character, or string literal, an enumeration constant, or a defined literal identifier 1.0, "test", 30, Color.Red
Identifier pattern A case value of a discriminated union, an exception label, or an active pattern case Some(x)

Failure(msg)
Variable pattern identifier a
as pattern pattern as identifier (a, b) as tuple1
OR pattern pattern1 | pattern2 ([h] | [h; _])
AND pattern pattern1 & pattern2 (a, b) & (_, "test")
Cons pattern identifier :: list-identifier h :: t
List pattern [ pattern_1; ... ; pattern_n ] [ a; b; c ]
Array pattern [| pattern_1; ..; pattern_n |] [| a; b; c |]
Parenthesized pattern ( pattern ) ( a )
Tuple pattern ( pattern_1, ... , pattern_n ) ( a, b )
Record pattern { identifier1 = pattern_1; ... ; identifier_n = pattern_n } { Name = name; }
Wildcard pattern _ _
Pattern together with type annotation pattern : type a : int
Type test pattern :? type [ as identifier ] :? System.DateTime as dt
Null pattern null null
Nameof pattern nameof expr nameof str
Do binding is used to execute code without defining a function or any type. You can write independent code by using do binding in F#.
 
Example :
type DoBindingClass() =   
 class  
  do printf "Hello FSharp"     
 end  
new DoBindingClass()  
 
F# allows type annotation so that you can explicitly mention the type of identifier or parameter or return type of a function. You must use : (colon) to apply annotation in F#.
 
Example : 
let a:int = 10  
printf "%d" a
The idea of type inference is that you do not have to specify the types of F# constructs except when the compiler cannot conclusively deduce the type. Omitting explicit type information does not mean that F# is a dynamically typed language or that values in F# are weakly typed. F# is a statically typed language, which means that the compiler deduces an exact type for each construct during compilation. If there is not enough information for the compiler to deduce the types of each construct, you must supply additional type information, typically by adding explicit type annotations somewhere in the code.
 
Inference of Parameter and Return Types
In a parameter list, you do not have to specify the type of each parameter. And yet, F# is a statically typed language, and therefore every value and expression has a definite type at compile time. For those types that you do not specify explicitly, the compiler infers the type based on the context. If the type is not otherwise specified, it is inferred to be generic. If the code uses a value inconsistently, in such a way that there is no single inferred type that satisfies all the uses of a value, the compiler reports an error.
 
The return type of a function is determined by the type of the last expression in the function.
 
For example, in the following code, the parameter types a and b and the return type are all inferred to be int because the literal 100 is of type int.
let f a b = a + b + 100
You can influence type inference by changing the literals. If you make the 100 a uint32 by appending the suffix u, the types of a, b, and the return value are inferred to be uint32.
 
You can also influence type inference by using other constructs that imply restrictions on the type, such as functions and methods that work with only a particular type.
 
Also, you can apply explicit type annotations to function or method parameters or to variables in expressions, as shown in the following examples. Errors result if conflicts occur between different constraints.
// Type annotations on a parameter.
let addu1 (x : uint32) y =
    x + y

// Type annotations on an expression.
let addu2 x y =
    (x : uint32) + y
You can also explicitly specify the return value of a function by providing a type annotation after all the parameters.
let addu1 x y : uint32 =
   x + y
A common case where a type annotation is useful on a parameter is when the parameter is an object type and you want to use a member.
let replace(str: string) =
    str.Replace("A", "a")
If the function code is not dependent on the type of a parameter, the compiler considers the parameter to be generic. This is called automatic generalization, and it can be a powerful aid to writing generic code without increasing complexity.
 
For example, the following function combines two parameters of any type into a tuple.
let makeTuple a b = (a, b)
The type is inferred to be
'a -> 'b -> 'a * 'b
F# provides conversion operators for arithmetic conversions between various primitive types, such as between integer and floating point types. The integral and char conversion operators have checked and unchecked forms; the floating point operators and the enum conversion operator do not. The unchecked forms are defined in FSharp.Core.Operators and the checked forms are defined in FSharp.Core.Operators.Checked.
let x : int = 5

let b : byte = byte x
The following table shows conversion operators defined in F#.

Operator Description
byte Convert to byte, an 8-bit unsigned type.
sbyte Convert to signed byte.
int16 Convert to a 16-bit signed integer.
uint16 Convert to a 16-bit unsigned integer.
int32, int Convert to a 32-bit signed integer.
uint32 Convert to a 32-bit unsigned integer.
int64 Convert to a 64-bit signed integer.
uint64 Convert to a 64-bit unsigned integer.
nativeint Convert to a native integer.
unativeint Convert to an unsigned native integer.
float, double Convert to a 64-bit double-precision IEEE floating point number.
float32, single Convert to a 32-bit single-precision IEEE floating point number.
decimal Convert to System.Decimal.
char Convert to System.Char, a Unicode character.
enum Convert to an enumerated type.

In addition to built-in primitive types, you can use these operators with types that implement op_Explicit or op_Implicit methods with appropriate signatures. For example, the int conversion operator works with any type that provides a static method op_Explicit that takes the type as a parameter and returns int. As a special exception to the general rule that methods cannot be overloaded by return type, you can do this for op_Explicit and op_Implicit.
The enum operator is a generic operator that takes one type parameter that represents the type of the enum to convert to. When it converts to an enumerated type, type inference attempts to determine the type of the enum that you want to convert to. In the following example, the variable col1 is not explicitly annotated, but its type is inferred from the later equality test. Therefore, the compiler can deduce that you are converting to a Color enumeration. Alternatively, you can supply a type annotation, as with col2 in the following example.
type Color =
    | Red = 1
    | Green = 2
    | Blue = 3

// The target type of the conversion cannot be determined by type inference, so the type parameter must be explicit.
let col1 = enum<Color> 1

// The target type is supplied by a type annotation.
let col2 : Color = enum 2
You can also specify the target enumeration type explicitly as a type parameter, as in the following code:
let col3 = enum<Color> 3
Note that the enumeration casts work only if the underlying type of the enumeration is compatible with the type being converted. In the following code, the conversion fails to compile because of the mismatch between int32 and uint32.
// Error: types are incompatible
let col4 : Color = enum 2u
A flexible type annotation indicates that a parameter, variable, or value has a type that is compatible with a specified type, where compatibility is determined by position in an object-oriented hierarchy of classes or interfaces. Flexible types are useful specifically when the automatic conversion to types higher in the type hierarchy does not occur but you still want to enable your functionality to work with any type in the hierarchy or any type that implements an interface.
 
Syntax : #type
 
 
In the previous syntax, type represents a base type or an interface.
 
A flexible type is equivalent to a generic type that has a constraint that limits the allowed types to types that are compatible with the base or interface type. That is, the following two lines of code are equivalent.
#SomeType

'T when 'T :> SomeType
Flexible types are useful in several types of situations. For example, when you have a higher order function (a function that takes a function as an argument), it is often useful to have the function return a flexible type. In the following example, the use of a flexible type with a sequence argument in iterate2 enables the higher order function to work with functions that generate sequences, arrays, lists, and any other enumerable type.
 
Consider the following two functions, one of which returns a sequence, the other of which returns a flexible type.
let iterate1 (f : unit -> seq<int>) =
    for e in f() do printfn "%d" e
let iterate2 (f : unit -> #seq<int>) =
    for e in f() do printfn "%d" e

// Passing a function that takes a list requires a cast.
iterate1 (fun () -> [1] :> seq<int>)

// Passing a function that takes a list to the version that specifies a
// flexible type as the return value is OK as is.
iterate2 (fun () -> [1])
As another example, consider the Seq.concat library function :
val concat: sequences:seq<#seq<'T>> -> seq<'T>
You can pass any of the following enumerable sequences to this function:
 
* A list of lists
* A list of arrays
* An array of lists
* An array of sequences
* Any other combination of enumerable sequences

The following code uses Seq.concat to demonstrate the scenarios that you can support by using flexible types.
let list1 = [1;2;3]
let list2 = [4;5;6]
let list3 = [7;8;9]

let concat1 = Seq.concat [ list1; list2; list3]
printfn "%A" concat1

let array1 = [|1;2;3|]
let array2 = [|4;5;6|]
let array3 = [|7;8;9|]

let concat2 = Seq.concat [ array1; array2; array3 ]
printfn "%A" concat2

let concat3 = Seq.concat [| list1; list2; list3 |]
printfn "%A" concat3

let concat4 = Seq.concat [| array1; array2; array3 |]
printfn "%A" concat4

let seq1 = { 1 .. 3 }
let seq2 = { 4 .. 6 }
let seq3 = { 7 .. 9 }

let concat5 = Seq.concat [| seq1; seq2; seq3 |]

printfn "%A" concat5
The output is as follows :
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
seq [1; 2; 3; 4; ...]
A tuple is a grouping of unnamed but ordered values, possibly of different types. Tuples can either be reference types or structs.
 
Syntax :
(element, ... , element)
struct(element, ... ,element )
The Value Option type in F# is used when the following two circumstances hold :
 
* A scenario is appropriate for an F# Option.
* Using a struct provides a performance benefit in your scenario.

Not all performance-sensitive scenarios are "solved" by using structs. You must consider the additional cost of copying when using them instead of reference types. However, large F# programs commonly instantiate many optional types that flow through hot paths, and in such cases, structs can often yield better overall performance over the lifetime of a program.
 
Definition : Value Option is defined as a struct discriminated union that is similar to the reference option type. Its definition can be thought of this way:
[<StructuralEquality; StructuralComparison>]
[<Struct>]
type ValueOption<'T> =
    | ValueNone
    | ValueSome of 'T
 
Value Option conforms to structural equality and comparison. The main difference is that the compiled name, type name, and case names all indicate that it is a value type.
 
Using Value Options : Value Options are used just like Options. ValueSome is used to indicate that a value is present, and ValueNone is used when a value is not present:
let tryParseDateTime (s: string) =
    match System.DateTime.TryParse(s) with
    | (true, dt) -> ValueSome dt
    | (false, _) -> ValueNone

let possibleDateString1 = "1990-12-25"
let possibleDateString2 = "This is not a date"

let result1 = tryParseDateTime possibleDateString1
let result2 = tryParseDateTime possibleDateString2

match (result1, result2) with
| ValueSome d1, ValueSome d2 -> printfn "Both are dates!"
| ValueSome d1, ValueNone -> printfn "Only the first is a date!"
| ValueNone, ValueSome d2 -> printfn "Only the second is a date!"
| ValueNone, ValueNone -> printfn "None of them are dates!"
As with Options, the naming convention for a function that returns ValueOption is to prefix it with try.
Value Option properties and methods : There is one property for Value Options at this time: Value. An InvalidOperationException is raised if no value is present when this property is invoked.
 
Value Option functions : The ValueOption module in FSharp.Core contains equivalent functionality to the Option module. There are a few differences in name, such as defaultValueArg:
val defaultValueArg : arg:'T voption -> defaultValue:'T -> 'T
It is an immutable collection of same type elements. It maintains the order of elements.
F# List Example
let list = [1;2;3;4;5;6;7;8;9]  
for i  in list do  
 printfn "%d" i  
Arrays are mutable collections of data of the same type. It starts from index 0 and goes to n-1 where n is the length of arrays.
 
Example : 
let arr = [| 1; 2; 3; 4; 5; |]               // Creating and initializing array  
for i = 0 to arr.Length-1 do                // Traversing of array  
  printfn "%d" arr.[i]  
The Sequence is a series of the same type of elements. It provides better performance than list.
 
Example : 
You can create sequence expression like following. Here, we have used Seq.iter () function to iterate sequence. We can also use for loop or array format specifier to iterate sequence elements.

let seq1 =  
 seq { 0 .. 10 }  
Seq.iter(fun a->printf " %d" a)seq1
Reference cells refer to memory locations. It allows you to create mutable values. F# uses immutable data structure by default.
 
Example :
let refVariable = ref 50  
printf "%d" refVariable.Value
By reviewing this topic, you can determine which F# collection type best suits a particular need. These collection types differ from the collection types in .NET, such as those in the System.Collections.Generic namespace, in that the F# collection types are designed from a functional programming perspective rather than an object-oriented perspective. More specifically, only the array collection has mutable elements. Therefore, when you modify a collection, you create an instance of the modified collection instead of altering the original collection.
 
Collection types also differ in the type of data structure in which objects are stored. Data structures such as hash tables, linked lists, and arrays have different performance characteristics and a different set of available operations.
 
Table of collection types :
The following table shows F# collection types.

Type Description Related Links
List An ordered, immutable series of elements of the same type. Implemented as a linked list. List Module
Array A fixed-size, zero-based, mutable collection of consecutive data elements that are all of the same type.
Array Module

Array2D Module

Array3D Module
seq A logical series of elements that are all of one type. Sequences are particularly useful when you have a large, ordered collection of data but don't necessarily expect to use all the elements. Individual sequence elements are computed only as required, so a sequence can perform better than a list if not all the elements are used. Sequences are represented by the seq<'T> type, which is an alias for IEnumerable. Therefore, any .NET Framework type that implements System.Collections.Generic.IEnumerable<'T> can be used as a sequence. Seq Module
Map An immutable dictionary of elements. Elements are accessed by key. Map Module
Set An immutable set that's based on binary trees, where comparison is the F# structural comparison function, which potentially uses implementations of the System.IComparable interface on key values. Set Module
The Object is a real-world entity. It can be anything like - cell phone, car, football, etc.
 
The Object is an instance of the class we can access all the members of the class by using object of this class.
 
Let's see an example of how to create an object in F#.
 
let objectName = new ClassName()
The Class is a template or blueprint of an object. It is used to encapsulate data members and member methods. It can contain fields, methods, constructor, static method, etc.
 
Example : 
type Student (id,name)=   
 class  
  member x.show =   
  printf "%d \n%s" id name   
  
 end  
let a = new Student (12,"FSharp")  
a.show 
In F#, Constructor is somewhat different than other .Net languages. There are always primary constructors that may or may not have parameters. The Scope of these parameters is throughout the class.
 
Example :
type Employee(name) =   
 class  
  do printf "%s" name  
 end  
let e = new Employee("FSharp")
In F#, a self is used to refer the current object of class type. Self is the same as this keyword in C# and Java. You can name the self-identifier however you want. You are not restricted to names such as this or self as in .Net languages.
 
Example :
type Employee(id,name) as this =  
    let id = id  
    let name = name  
    do this.Display()           // This is how we can use self(this) object  
    member this.Display() =  
        printf "%d %s" id name  
let e =new Employee(100, "Ramana")
In F#, static is a keyword. It is used to make the static field or static method. Static is not the part of the object. It has its memory space to store static data. It is used to share common properties among objects.
 
Example :
type Account(accno,name) = class  
 static let rateOfInterest = 8.8  
 member this.display()=  
  printfn "%d %s %0.2f" accno name rateOfInterest   
end  
let a1 = new Account(101,"Rajkumar")  
let a2 = new Account(102, "john")  
a1.display()  
a2.display() 
Inheritance is used to model the "is-a" relationship, or subtyping, in object-oriented programming.
 
Specifying Inheritance Relationships :
You specify inheritance relationships by using the inherit keyword in a class declaration. The basic syntactical form is shown in the following example.
type MyDerived(...) =
    inherit MyBase(...)
A class can have at most one direct base class. If you do not specify a base class by using the inherit keyword, the class implicitly inherits from System.Object.

Inherited Members :If a class inherits from another class, the methods and members of the base class are available to users of the derived class as if they were direct members of the derived class.
 
Any let bindings and constructor parameters are private to a class and, therefore, cannot be accessed from derived classes.
 
The keyword base is available in derived classes and refers to the base class instance. It is used like the self-identifier.
 
Virtual Methods and Overrides : Virtual methods (and properties) work somewhat differently in F# as compared to other .NET languages. To declare a new virtual member, you use the abstract keyword. You do this regardless of whether you provide a default implementation for that method. Thus a complete definition of a virtual method in a base class follows this pattern:
abstract member [method-name] : [type]

default [self-identifier].[method-name] [argument-list] = [method-body]
And in a derived class, an override of this virtual method follows this pattern:
override [self-identifier].[method-name] [argument-list] = [method-body]
If you omit the default implementation in the base class, the base class becomes an abstract class.
 
The following code example illustrates the declaration of a new virtual method function1 in a base class and how to override it in a derived class.
type MyClassBase1() =
   let mutable z = 0
   abstract member function1 : int -> int
   default u.function1(a : int) = z <- z + a; z

type MyClassDerived1() =
   inherit MyClassBase1()
   override u.function1(a: int) = a + 1
Constructors and Inheritance : The constructor for the base class must be called in the derived class. The arguments for the base class constructor appear in the argument list in the inherit clause. The values that are used must be determined from the arguments supplied to the derived class constructor.
 
The following code shows a base class and a derived class, where the derived class calls the base class constructor in the inherit clause:
type MyClassBase2(x: int) =
   let mutable z = x * x
   do for i in 1..z do printf "%d " i


type MyClassDerived2(y: int) =
   inherit MyClassBase2(y * 2)
   do for i in 1..y do printf "%d " i
In the case of multiple constructors, the following code can be used. The first line of the derived class constructors is the inherit clause, and the fields appear as explicit fields that are declared with the val keyword. For more information, see Explicit Fields: The val Keyword.
type BaseClass =
    val string1 : string
    new (str) = { string1 = str }
    new () = { string1 = "" }

type DerivedClass =
    inherit BaseClass

    val string2 : string
    new (str1, str2) = { inherit BaseClass(str1); string2 = str2 }
    new (str2) = { inherit BaseClass(); string2 = str2 }

let obj1 = DerivedClass("A", "B")
let obj2 = DerivedClass("A")
Alternatives to Inheritance : In cases where a minor modification of a type is required, consider using an object expression as an alternative to inheritance. The following example illustrates the use of an object expression as an alternative to creating a new derived type:
open System

let object1 = { new Object() with
      override this.ToString() = "This overrides object.ToString()"
      }

printfn "%s" (object1.ToString())
Abstract classes are classes that leave some or all members unimplemented, so that implementations can be provided by derived classes.
 
Syntax : 
// Abstract class syntax.
[<AbstractClass>]
type [ accessibility-modifier ] abstract-class-name =
[ inherit base-class-or-interface-name ]
[ abstract-member-declarations-and-member-definitions ]

// Abstract member syntax.
abstract member member-name : type-signature
Method overriding is a feature of Object-oriented programming approach. It helps to achieve polymorphism. We can achieve method overriding using inheritance.
 
Example :
type Employee() =  
 class  
  abstract ShowName : unit -> unit  
  default this.ShowName() = printfn"This is base class method"  
 end   
type Manager() =  
 class  
  inherit Employee()  
  override this.ShowName() = printf "This is derived class method"  
 end  
  
let employee = new Employee()  
let manager = new Manager()  
employee.ShowName()  
manager.ShowName()
This topic describes how to overload arithmetic operators in a class or record type, and at the global level.
 
Syntax :
// Overloading an operator as a class or record member.
static member (operator-symbols) (parameter-list) =
    method-body
// Overloading an operator at the global level
let [inline] (operator-symbols) parameter-list = function-body
In the previous syntax, the operator-symbol is one of +, -, *, /, =, and so on. The parameter-list specifies the operands in the order they appear in the usual syntax for that operator. The method-body constructs the resulting value.
 
Operator overloads for operators must be static. Operator overloads for unary operators, such as + and -, must use a tilde (~) in the operator-symbol to indicate that the operator is a unary operator and not a binary operator, as shown in the following declaration.
 
static member (~-) (v : Vector)
When the F# compiler compiles an operator expression, it generates a method that has a compiler-generated name for that operator. This is the name that appears in the Microsoft intermediate language (MSIL) for the method, and also in reflection and IntelliSense. You do not normally need to use these names in F# code.
 
The following table shows the standard operators and their corresponding generated names.

Operator Generated name
[] op_Nil
:: op_Cons
+ op_Addition
- op_Subtraction
* op_Multiply
/ op_Division
@ op_Append
^ op_Concatenate
% op_Modulus
&&& op_BitwiseAnd
||| op_BitwiseOr
^^^ op_ExclusiveOr
<<< op_LeftShift
~~~ op_LogicalNot
>>> op_RightShift
~+ op_UnaryPlus
~- op_UnaryNegation
= op_Equality
<= op_LessThanOrEqual
>= op_GreaterThanOrEqual
< op_LessThan
> op_GreaterThan
? op_Dynamic
?<- op_DynamicAssignment
|> op_PipeRight
<| op_PipeLeft
! op_Dereference
>> op_ComposeRight
<< op_ComposeLeft
<@ @> op_Quotation
<@@ @@> op_QuotationUntyped
+= op_AdditionAssignment
-= op_SubtractionAssignment
*= op_MultiplyAssignment
/= op_DivisionAssignment
.. op_Range
.. .. op_RangeStep
F# provides Interface type. It provides pure abstraction. It is a collection of abstract methods.
 
Example :
type IEmployee =  
   abstract member ShowName : unit -> unit  
type Manager(id:int, name:string) =  
   interface IEmployee with  
      member this.ShowName() = printfn "Id = %d \nName = %s" id name  
  
let manager = new Manager(100,"RajKumar")  
//manager.ShowName()    // error: you can't directly access interface abstract method by using class object.  
// so, we need to upcast class type object to interface type by using :> operator.  
(manager :> IEmployee).ShowName()
In F#, delegates are reference types. It allows us to call the function as an object. It is a feature of this language. It gives an advantage over the other functional programming languages.
 
Example :
type Deligate() =  
  static member mul(a : int, b : int) = a * b  
  member x.Mul(a : int, b : int) = a * b  
type Multiply = delegate of (int * int) -> int  
  
let getIt (d : Multiply) (a : int) (b: int) =  
   d.Invoke(a, b)  
let d : Multiply = new Multiply( Deligate.mul )  
for (a, b) in [(5, 8) ] do  
  printfn "%d * %d = %d" a b (getIt d a b)
In F#, the signature file contains information about the public signatures. Signatures can be of a set of program elements, such as types, namespaces, and modules.
Signature file named as signature.fsi
namespace FSharpPrograms  
  module Arithmetic =  
    val add : int * int -> int  
    val sub : int * int -> int  
An import declaration specifies a module or namespace. You can reference its elements without using a fully qualified name.
 
Example :
open System  
Console.WriteLine("Hello, this is F# here.") 
Lazy expressions are expressions that are not evaluated immediately, but are instead evaluated when the result is needed. This can help to improve the performance of your code.
 
Syntax : let identifier = lazy ( expression )

In the previous syntax, expression is code that is evaluated only when a result is required, and identifier is a value that stores the result. The value is of type Lazy<'T>, where the actual type that is used for 'T is determined from the result of the expression.
 
Lazy expressions enable you to improve performance by restricting the execution of an expression to only those situations in which a result is needed.
 
To force the expressions to be performed, you call the method Force. Force causes the execution to be performed only one time. Subsequent calls to Force return the same result, but do not execute any code.
 
The following code illustrates the use of lazy expressions and the use of Force. In this code, the type of result is Lazy<int>, and the Force method returns an int.
let x = 10
let result = lazy (x + 10)
printfn "%d" (result.Force())

Lazy evaluation, but not the Lazy type, is also used for sequences.

In F#, you can produce documentation from triple-slash (///) code comments. XML comments can precede declarations in code files (.fs) or signature (.fsi) files.
 
Example :
let add x y  = x+y  
let result = lazy (add 10 10)  
printfn "%d" (result.Force())
By using Caller Info attributes, you can obtain information about the caller to a method. You can obtain file path of the source code, the line number in the source code, and the member name of the caller. This information is helpful for tracing, debugging, and creating diagnostic tools.
 
To obtain this information, you use attributes that are applied to optional parameters, each of which has a default value. The following table lists the Caller Info attributes that are defined in the System.Runtime.CompilerServices namespace:

Attribute Description Type
CallerFilePath Full path of the source file that contains the caller. This is the file path at compile time. String
CallerLineNumber Line number in the source file at which the method is called. Integer
CallerMemberName Method or property name of the caller. See the Member Names section later in this topic. String

The following example shows how you might use these attributes to trace a caller.
open System.Diagnostics
open System.Runtime.CompilerServices
open System.Runtime.InteropServices

type Tracer() =
    member _.DoTrace(message: string,
                      [<CallerMemberName; Optional; DefaultParameterValue("")>] memberName: string,
                      [<CallerFilePath; Optional; DefaultParameterValue("")>] path: string,
                      [<CallerLineNumber; Optional; DefaultParameterValue(0)>] line: int) =
        Trace.WriteLine(sprintf $"Message: {message}")
        Trace.WriteLine(sprintf $"Member name: {memberName}")
        Trace.WriteLine(sprintf $"Source file path: {path}")
        Trace.WriteLine(sprintf $"Source line number: {line}")
This article describes the command-line options supported by F# Interactive, fsi.exe. F# Interactive accepts many of the same command-line options as the F# compiler, but also accepts some additional options.
 
Use F# Interactive for scripting : F# Interactive, dotnet fsi, can be launched interactively, or it can be launched from the command line to run a script.

The command-line syntax is :
dotnet fsi [options] [ script-file [arguments] ]
The file extension for F# script files is .fsx.