Overview of F# Development and Data Types
F# was developed by Microsoft as part of its .NET platform, focusing on functional programming. It is a multi-paradigm language with emphasis on functional programming. F# is strongly typed with type inference and influenced by languages like ML, C#, Python, and Haskell. The language is platform-independent, making it interoperable with other languages like C#. Data types in F# include integers, floats, strings, chars, bools, and more. Built-in data structures like tuples, records, discriminated unions, and maps are also supported. Additionally, computations in F# are expressed using various data structures, variables, and computation expressions.
Download Presentation

Please find below an Image/Link to download the presentation.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.If you encounter any issues during the download, it is possible that the publisher has removed the file from their server.
You are allowed to download the files provided on this website for personal or commercial use, subject to the condition that they are used lawfully. All files are the property of their respective owners.
The content on the website is provided AS IS for your information and personal use only. It may not be sold, licensed, or shared on other websites without obtaining consent from the author.
E N D
Presentation Transcript
F# F# was developed by MS as part of its .net platform as the functional language of the platform It has been further developed by the F# Software Foundation and the open source community F# is a multi-paradigm language: functional, imperative, OO the biggest emphasis is on it as a functional programming language and that s primarily what we study here it is a strongly typed language using type inference as much as possible there are some instances where you must explicitly type your variables and parameters but usually not it is platform independent so that its programs can work with programs of other languages (e.g., C# programs) it is somewhat of a descendant of the functional language ML (more precisely, it descended from Ocaml which is based on ML) it has also been influenced by languages like C#, Python and Haskell
Data Types All variables and parameters are typed you can explicitly type them but under most circumstances, the compiler or interpreter will infer the type so the type can be omitted Here are the basic types byte, sbyte unsigned, signed 1 byte integer uint16, int16 unsigned, signed 2 byte integer uint32, int32 (also just int for short) unsigned, signed 4-byte integer uint64, int64 unsigned, signed 8-byte integer bigint any integer (at least 4 bytes) float32, float, decimal 32 and 64 bit floats, 16 byte float BigRational at least 4 bytes char, string 2 bytes, any size (up to about 2 billion characters) bool stored in 1 byte There is no implicit casting available even with safe conversions like int to int64
Built-in Data Structures unit used as the return value from what would normally be void functions Variables when declared, may be typed but usually type is inferred variables can store any primitive type (see last slide) or any of these types, or functions Tuples like Python s, non-homogenous structure Record like tuples except individual values have names Discriminated unions Maps, Sets immutable types Arrays, Lists note that both arrays and lists are homogenous structures unlike tuples, and lists are also immutable Sequences lazy list of items, computed on-demand, for instance seq { 1 .. 10 } a third value, inserted between the two, is the step size as in { 1 .. 2 .. 10 } generates the sequence 1, 3, 5, 7, 9 sequences can contain a for loop NOTE: in all cases, data structures and variables are immutable unless explicitly made mutable (we cover this later) Computation expressions see next slide
Computation Expressions F# treats functions as first class citizens meaning that you can (and must) bind them to names (unless they are defined as lambda expressions, covered later) functions can be handled much like variables in that they can be passed to other functions being first class citizens, they can also be returned from functions Example let add_one x = x + 1 x it the parameter, the function returns x + 1 let negate x f = -1 * f(x) x is a numeric value, f is a function, this returns the negation of f(x) we can also define negate using let negate x f = -1 * (f x) This is similar to what LISP has available, so what is the significance? the best usage is when dealing with multithreaded code if a function is to be called in an unpredictable manner, we can pass to it some other function which we want executed for this particular instance
Input/Output The easiest form of output is printf/printfn (the n forces a \n at the end) Format: printf specification string values the specification string is like C s printf specification string and can contain both literal characters and specifiers (%i for int, %f for float, %s for string, %c for char, %b for boolean, others are available, use %a to mean infer the type ) the values can be literals, variables and results of function calls the values are separated by spaces, not commas example: printfn Hello %s, you are %i years old name (year birth) The Console class is also available for input and output Console.ReadLine() returns the latest input value let x = Console.ReadLine(); Console.ReadLine() |> ignore this statement causes the return value to be ignored Console.WriteLine( Hello {0}, you are {1} years old (name, year birth)) to use Console, you need to do open System (similar to an import statement) printf has more precise formatting but is supposedly slower than Console I/O
Binding All variables and functions must be bound except for lambda functions Three ways to bind, let is the common way let x = 0 let a, b = 5, hello let foo x y = (code for foo with parameters x and y) The word use does the same except that when the variable is no longer in scope, it is disposed of automatically using the Dispose() method The word do can be used to execute code without first defining it we will use let most of the time Example for defining a function let somefunction x = let a = x + 1 let b = a * 3 (x + b) % 5 This function returns (x + (x + 1) * 3) % 5
Operators Arithmetic: +, -, *, /, %, ** (exponent) Relational: = (equality), <, >, <=, >=, <> (not equal) Boolean: not, &&, || Bitwise: &&&, |||, ^^^ (xor), ~~~ (not), <<<, >>> (left/right shift) (5 <<< 2) = 20 (5 or 101 left shifted 2 bits becomes 20 or 10100) consider the following let x = 5 (x is 000 00101) ~~~x returns -6 (this is 111 11010) Operator overloading is available when dealing with classes but not with primitive types Variables are typically immutable let x = 5 let x = x + 1 // this is a different x, the old x is gone let mutable x = 5 x x + 1 // this is the same x with a new value, use <- for mutables
If-Then-Else Statement A function that must return a value returns the value of the function executed in the then clause or the value of the function executed in the else clause depending on whether the condition is true or false since it is a function that returns a value, there must be two clauses the if-then statement is illegal except in the circumstance when the then clause returns unit (void) if x > 0 then x + 1 else x 1 if x > 0 then x + 1 illegal if x > 0 then printfn %i x legal because this returns unit you can have a nested if-then-else using elif (there must still be a last else) if x > 0 then x + 1 elif x = 0 then -1 else x + 5
Another Conditional: match The match operation is similar to a switch statement Form: match expression with | pattern -> operation | pattern -> operation | _ -> operation the _ is a default and can be omitted Simple example: let function1 x y = match y with | 0 -> x | 1 -> x +1 | 2 -> x 1 | _ -> y function1 returns x if y = 0, x+1 if y = 1, x-1 if y = 2, and y otherwise
Loops There are two types of loops for loops while loops we can skip for loops by generating sequences For loop: for variable = first to last do statement(s) for variable in sequence do statement(s) example: let mutable total = 0 for i = 1 to 10 do total <- total + i While loop: while condition do statement(s) Why not use loops? Loops are functions which return unit so there is no way to return a value from inside a loop anything performed inside the loop statement(s) must return unit to avoid a syntax error there is no equivalent of a break or continue in F# to easily get out of a loop // without making total mutable, this // code will continually create a new // variable for total
Sequences One type of structure is a sequence which we generate as seq { } where contains the code to generate the sequence the can be number .. number as in 0 .. 10 number .. number .. number where the second number is the step size as in 0 .. 2 .. 10 (generates the sequence 0, 2, 4, 6, 8, 10) note: in these two cases, the seq can be omitted, see below a for loop statement which includes either the word yield or the -> the yield/ -> causes a value to be returned (recall a for loop is a function which returns unit) Examples let x = seq { 0 .. 10 } let y = { 0 .. 10 } (omitting the seq ) let z = seq { 0 .. 2 .. 10 } let q = seq { for i in 1 .. 10 do yield i * i } q will be the sequence of {1, 4, 9, , 100} this can also be written as let q = seq { for i in 1 .. 10 -> i * i }
More on Sequences The sequence generated is represented using the notation [ first value; second value; third value; ; last value ] as in [ 1; 4; 9; 16; 25; 36; 49; 64; 81; 100 ] There is a sequence class that can operate on sequences the class is called Seq Useful Seq methods include Seq.take obtain the first x values of the sequence such as the first 10 values seq { 1 .. 1000 } |> Seq.take 10 note: |> is a pipe operator which takes the sequence and passes it to the Seq.take method along with the argument 10, we discuss |> shortly Seq.iter iterate over each item in the sequence, applying the given function/operation this is similar to using a for-loop from within a seq statement Seq.iter (printfn %i ) (seq { 1 .. 2 .. 10 }) Seq.compareWith takes two sequences and returns 1 (first sequence > second sequence), 0 (equal), -1 (first < second)
Tuples Much like Python s except there are two types normal tuples are referenced by a reference variable struct tuple is a full structure like C s structs note: if you use struct in an assignment, both LHS and RHS have to be struct tuples Tuples are enclosed in ( ) and can consist of items of any type You can assign one variable to be a tuple let x = (1, abc , 3.5) tuples have a signature, this tuple s signature is int * string * float You can assign multiple variables to take on the values within a tuple let (x, y) = (1, abc ) // x = 1, y = abc You can bypass values using _ in place of a variable as in let (x, _, y) = (a, abc , 3.5) // x = 1, y = 3.5 without using _, if the LHS and RHS have different numbers, you get an error built-in tuple operations: fst (get first item), snd (get second item), = (equality) (fst and snd only work if the tuple has exactly 2 items in it)
Records Like tuples except each element (member) has a name tagged to it You also must type your elements when defining the record type Person = { Age : int; Name : string; GPA : float } let frank = {Age = 53; Name = Frank ; GPA = 3.513 } Access elements using var.member as in frank.Age, frank.Name, etc Elements can be made mutable by adding mutable before the member s name as in type Person = { mutable Age : int; Name : string; mutable GPA : float } Records can be compared using = and you can use match to perform pattern matching
Lists Lists are denoted in [ ] and you can create an empty list using [] You can define numeric sequences in a list using the same notation as with sequences let list1 = [ 1 .. 2 .. 10 ] let list2 = [ for i in 1 .. 10 yield i * i ] You can also form a list by placing yield in front of each value as in let list3 = [ yield 1; yield 2; yield 3 ] // list3 = [ 1; 2; 3 ] Many built-in operations obtainable using list1.operation as in list2.Head return first item in list2 (like LISP s CAR) list2.Empty returns an empty list list2.Length returns length of list list2.Item(i) returns the ith element of list2 where the first element is at 0 list2.Tail returns the list without the first item (like LISP s CDR) list2.Tail.Tail.Length return the length of list2 without the first 2 items
The List Class List has several useful methods that can be invoked on any list List.iteri iterate over the given list List.find (function) list return the first element of the list in which the function returns true List.filter (function) list return the list of all elements in the given list for which the function returns true List.sortWith sort the given list Imagine we have a function called even which, when passed x, returns true if x is even List.filter (even) someList returns the values of someList in a list for all those values that are even
Arrays Array notation is like the list but adds | | as in [| 1; 2; 3 |] Accessing array values is done through var.[index] let x = [| 1; 2; 3; 4; 5 |] x.[0] returns 1 You can create an array of arrays using [| [| 1; 2; 3 |]; [| 4; 5; 6 |] |] To create 2 and 3 arrays, you have to use built-in operations, for instance let array2 = array2D [ [ 1; 2; 3; 4 ]; [ 5; 6; 7; 8 ] ] Array values are mutable and changed using the <- notation as in x.[1] <- x.[1] + 1
Slices F# arrays can be operated on in slices a slice will define a range of values which may specify the lower bound only, as in [4..] to be elements 4 and up the upper bound only, as in [..6] to be selects 0 through 6 the upper and lower bounds as in [3..6] to be elements 3 through 6 a wildcard, as in [*], to indicate the entire array Example: let arr = [|1..10|] (note that arr.[0] is 1) arr.[4..] returns the array of elements 4 through 9, which are the numbers 5-10 arr.[..6] returns the array of elements 0 through 6, which are the numbers 1-7 arr.[3..6] returns the array of elements 3 through 6 which are numbers 4-7 arr.[*] returns the entire array You can similarly obtain slices for 2D arrays and beyond, such as array2D.[*,0] which returns the first 1D array array2D.[0..3,0..3] returns the 2D subarray as expected
Array Methods find, findIndex returns the first element/index of the first element for which the given function returns true Array.find (fun x -> x < 0) arr Array.findIndex (fun x -> x < 0) arr returns the first element of arr which is < 0, returns the index of the first element of arr < 0 causes an error if no array value results in the function being true forall tests every element of an array against a boolean function and returns true if all elements result in the function being true, false otherwise Array.forall (fun x -> x > 0) arr returns true if all elements of arr are positive partition splits array into two based on which elements make a function true and which make it false Array.partition (fun x -> x % 2 = 0) arr this returns two arrays, the even elements and the odd elements
Returning to Match Recall in match, we enumerated possible values to compare against a variable We can also use var when condition -> result where condition tests the variable Consider the following which returns 0 if x is 0, -1 if x < 0, and +1 if x > 0 match x with | 0 -> 0 | x when x < 0 -> -1 | _ -> 1 When the variable is a tuple, we can do more complex pattern matching match x with // assume x is a tuple of two int values | (a, b) when a > b -> a | (a, b) when a < b -> b | _ -> 0 this returns the larger of the two or 0 if they are the same An even more complex situation is permissible when you use notation like | (a, b) & (0, _) which says if a = 0 and b does not matter
Functions Let s focus on functions now functions return a value or return the value unit You define a function using let functionname params = functionbody You can define a nameless function (lambda function) using the fun keyword and -> instead of = fun params -> functionbody this let s you combine the two to define a function as follows let functionname = fun params -> functionbody You can explicitly type your parameters using notation (param : type) as in let squareIt (x : float) = x * x normally x * x would be interpreted as an int, so this changes the function s behavior to force the parameter to be a float and to return a float To explicitly control the return type, add : type before = as in let computeArea height width : float = height * width note: this requires both height and width to be floats!
Functions Which Call Functions In C, we might do sqrt(abs(x)) In F#, it looks like this: sqrt(abs x) We can also accomplish this using a pipe (|>) as in abs x |> sqrt this takes the result of the function call abs x and passes it to the function sqrt there is also a backward pipe <| which we could apply as sqrt <| abs x We can compose two functions together as follows let legalsqrt = abs |> sqrt legalsqrt accepts one parameter (not shown here because abs expects one parameter), applies abs to that parameter and then applies sqrt to the result NOTE: F# does not have abs or sqrt, we would have to write our own
More on Functions To specify that a function is recursive, add the reserved word rec after let let rec functionname params = functionbody example: let rec fib x = if x <= 2 then 1 else fib (x 1) + fib (x 2) note that we normally do not need ( ) around our parameters, but here we are forcing the arithmetic expression to execute prior to the function call or else we would be calling fib with x and this would be a case of infinite recursion! To specify a parameterless function, add ( ) after the function name let functionname() = The reason for this is that without (), F# thinks this will be a normal assignment statement To receive a tuple, place the params in ( ) let functionname (firstvalue, secondvalue, thirdvalue) = To receive a record, place the params and member names in { } as in let functionname { firstMember = f; secondMember = s } = (here reference f and s)
Function Signatures When you define a function, the return value is its signature a signature is the parameter profile including the return type you can use the signature to determine if the compiler/interpreter has correctly inferred the parameter types example: let addone x = x + 1 this returns val addone : x:int -> int meaning that addone receives an int parameter and returns an int example: let sum3 x y z = x + y + z this returns val sum3 : x:int -> y:int -> z:int -> int If a function has no parameters, then the signature is unit -> returntype and if a function returns no value, then it is params -> unit as in x:int -> unit When we use composition (that is, a function which receives a function), the signature is more complicated here we have a function which receives a function and a param, applies the function to the param and doubles the result let doubleIt f x = 2 * (f x) this returns a more interesting signature: val double : f( a -> int) -> x: a -> int this means that the first parameter is a function which receives an int parameter, labeled x, and returns an int the is used to indicate a generic type of paramter
Example F# Code Here is how we can use iteration to define a prime number function let isprime x = let mutable divisor = 2 let mutable prime = true while divisor < x && prime do if x % divisor = 0 then prime <- false else divisor <- divisor + 1 prime Notice how this function uses two mutable local variables, let s instead write this with recursion let rec isprime x divisor = if x = divisor then true else if x % divisor = 0 then false else isprime x (divisor + 1) not only is this code more concise but it does away with the mutable variables Let s use the List method of forall let isprime x = [ 2 .. x-1 ] |> List.forall (fun n -> x % n <> 0)