Learning F Sharp by Example

  1. Background

    F# is a new functional language from Microsoft based on OCaml.

  2. A few interesting points about the language:
    1. F# is multi-paradigm, meaning it can be used in a functional, imperative, or object oriented manner.
    2. It is statically typed giving some protection against mixing object types
    3. F# runs on the CLR and can access all the wonderful libraries in .Net and your custom-built .Net objects.
    4. You can write F# modules accessible to your .Net applications.
    5. F# shines in compute intensive applications where multi-processors and multi-threading can speed results.
    6. Type inference makes your code smaller and more flexible.

    You can download F# from here although its installed automatically with Visual Studio 2010.

  3. Getting Started
    1. Our first program.

      Open your favorite text editor and enter this into a file named "HelloWorld.fs".

      printfn "Hello World"
      

      Since I like to compile the first program by hand, let's compile it in a console window, then execute it.

      C:\home\mfincher\fsharp>"C:\Program Files\Microsoft F#\v4.0\fsc.exe" HelloWorld.fs
      Microsoft (R) F# 2.0 Compiler build 4.0.30319.1
      Copyright (c) Microsoft Corporation. All Rights Reserved.
      
      C:\home\mfincher\fsharp>HelloWorld.exe
      Hello World
      

      You've just written your first F# program. OK, no big deal really.

    2. F# program in Visual Studio

      In VS select "File/New Project/Visual F#/F# Application" and enter this program:

      open System
      [<EntryPoint>]
      let main (args : string[]) =
          if args. Length <> 3 then
              failwith "Usage: HiThere.exe firstName lastName city"
          let firstName, lastName, city = args.[0], args.[1], args.[2]
          printfn "Hi There %s %s from %s" firstName lastName city
          0
      

      Now set the parameters be selecting "Project/<YourApplicationName> Properties/Debug/Start Options Command line parameters:" and enter "Percy Jackson NewYork"

      Enter "Ctl-F5" to run your application.

      Hi There Percy Jackson from NewYork
      Press any key to continue . . .
      

      The "0" at the end is the status value returned to the operating system.

    3. Let

      "let" binds a symbol to an immutable (cannot be changed) value.

      In languages like java and c# when you assign a primitive type like a double to a value, you are copying a value into the "box" that contains the value of the variable.

      Consider the C# code below:

      double total;
      total = 100.0;
      total = 200.0;
      

      "double total" allocates 8 bytes of memory to hold a value and sets the variable "total" to point to that chunk of memory. "total = 100.0" copies the constant "100.0" into that bit of memory. "total = 200.0" overwrites the memory with a new value, "200.0". "100.0" is now gone. "total" still points to the same bit of memory, but the contents of that memory have changed.

      In contrast, let's look at this F# code:

      > let total = 100.0;;
      > let total = 200.0;;
      

      It looks similar, but behind the scenes life is different in Functional Programming Land. The first line allocates a bit of memory and copies "100.0" into that memory. Then it sets the memory associated with the symbol "total" to point to that memory containing "100.0".

      When the second line is executed, F# allocates a new bit of memory and copies "200.0" into it and then makes "total" point to the new memory location. "100.0" still exists in memory, but "total" now points to the memory with "200.0" in it.

      Since a value is not overwritten, it makes parallel programming easier since you can't overwrite a value another thread is depending on.

      Psst, by the way, you can assign multiple values with one "let"

      > let dog, cat = "bark", "meow";;
      
      val dog : string = "bark"
      val cat : string = "meow"
      
    4. Mutable assignments

      Although in functional programming we generally want immutable objects, F# allows mutable values,

      > let mutable total = "100.0";;
      
      val mutable total : string = "100.0"
      
      > total <- "200.0";; //destructive assignment operator
      val it : unit = ()
      

      The contents of the memory that "total" points to is now overwritten using the "destructive assignment operator", "<-".

    5. Variable Names

      F# variable names are composed of letters, numbers, underbar "_", and apostrophe "'", but starts with a letter or underscore. Variable and function names are case sensitive.

    6. Whitespace

      In F# whitespace matters. Instead of using curly braces blocks of code are indented. To avoid fights about tabs being 4 or 5 spaces, tabs are banished from the language.

      The block of code under a key work like "if" must start to the right of "if" so F# knows where the block starts.

    7. Comments

      F# has three types of comments:

      (* 
       I am a multi-line
       comment
       *)
       //I am a single line comment
       ///I am a documentation comment
      
    8. F# Interactive (FSI) Window

      To play with snippets of code you can enter the F# Interactive Window inside VS2010 by entering "Ctl-Alt-F" (or if you have resharper installed taking that key combination, you can use "View/Other Windows/F# Interactive" until you remap it). Enter code then ";;" followed by a blank line.

      Microsoft (R) F# 2.0 Interactive build 4.0.30319.1
      Copyright (c) Microsoft Corporation. All Rights Reserved.
      
      For help type #help;;
      
      > let a = "asdf";;
      
      val a : string = "asdf"
      
      > 
      

      To remove the clutter from a session, right-click in the window and select, "Reset Session".

      This type of interaction is called a read-eval-print loop (REPL.) popularized in LISP.

      You can also use the command line version of fsi by invoking it directly in a console. My fsi.exe is at "C:\Program Files\Microsoft F#\v4.0\fsi.exe".

      Use the "#help;;" directive to list other directives inside fsi.

      > #help;;
      
        F# Interactive directives:
      
          #r "file.dll";;        Reference (dynamically load) the given DLL
          #I "path";;            Add the given search path for referenced DLLs
          #load "file.fs" ...;;  Load the given file(s) as if compiled and referenced
          #time ["on"|"off"];;  Toggle timing on/off
          #help;;                Display help
          #quit;;                Exit
      
        F# Interactive command line options:
      
            See 'fsi --help' for options
      
    9. Accessing the .Net libraries

      One of the great things about F# is the ability to access the ginormous library of .Net objects. Use the "open" keyword to access elements in a library without having to use a fully qualified name. Inside the FSI:

      > open System
      let ran = new Random()
      let x = ran.Next();;
      
      val ran : Random
      val x : int = 1762269880
      

      Another example of accessing .Net libraries, but using a fully qualified name and not the "open" keyword.

      printfn "dir=%s" System.Environment.CurrentDirectory
      

      If you "open" two namespaces that have identically named elements F# picks the last one opened as the winner.

  4. Types
    1. F# supports all our old friends in the .Net framework,
      TypeSuffix.NET typeRange
      byteuySystem.Byte0 to 255
      sbyteySystem.SByte-128 to 127
      int16sSystem.Int16-32,768 to 32,767
      uint16usSystem.UInt160 to 65,535
      int, int32 System.Int32-2^31 to 2^31-1
      uint, uint32uSystem.UInt320 to 2^31-1
      int64LSystem.Int64-2^63 to 2^63-1
      uint64ULSystem.UInt640 to 2^64-1
      native intn System.IntPtrsystem dependent
      unsigned native intun System.IntPtrsystem dependent
      float or double(optional) e System.Double15 significant digits
      float32fSystem.Float7 significant digits
      decimalMSystem.Decimal28 digits of precision
      bignumISystem.FSharp.Math.BigIntarbitrary precision
    2. Specifying type

      You can specify a type for a constant by adding its suffix to a number

      > let NationalDebt = 14000000000000M;;
      
      val NationalDebt : decimal = 14000000000000M
      
      > let DebtPerPerson = 44080.57f;;
      
      val DebtPerPerson : float32 = 44080.5703f
      

      You can also specify the type of a value explicitly

      > let x = 100.0
      let y:double = 200.0;;
      
      val x : float = 100.0
      val y : double = 200.0
      
    3. Quiet overflow

      When a numeric type gets to big, it quietly overflows and rolls over like an odometer (well at least like the old analog odometers). Below we see an unsigned byte (whose largest value is 255) get overflowed. To get around this use bigger types or use checked arithmetic.

      > let IAmAByte = 250uy;;
      
      val IAmAByte : byte = 250uy
      
      > let IAmTooBig = IAmAByte + 30uy;;
      
      val IAmTooBig : byte = 24uy
      
      
    4. Specifying numbers in other bases

      F# uses the common syntax of prefacing hex numbers with "0x", Octal with "0o" (zero followed by "o"), and binary with "0b" or "0B".

    5. BigInt

      For really, really big integers use BigInt. To calculate the number of protons in the universe, the Eddington Number, just put "I" at the end of the number.

      > let Eddington = 1000000000000000000000000000000000000000000000000000000000000000000000000000000000I
      ;;
      
      val Eddington : Numerics.BigInteger =
        1000000000000000000000000000000000000000000000000000000000000000000000000000000000
      
    6. Math operators

      F# uses the standard math operators, "+","-","*","/","%" modulus,"**" power (e.g., 2.0 ** 3.0, only for floating point.)

    7. Math functions

      The common math functions are supported: abs, ceil, exp, floor, log, sqrt, cos, sin, tan, pown (power for an integer).

    8. Boolean operators

      The usual suspects: "&&" and, "||" or, "not".

    9. Strings

      All strings are UTF-16 double-byte characters. Strings can be defined across lines, if the line ends with a backslash. On the second line, all leading whitespace is removed. To access individual characters, use the ".[n]" syntax.

      > let vowels = "ae\
                          iou\
                          y";;
      
      val vowels : string = "aeiouy"
      
      > vowels.[2]
      ;;
      val it : char = 'i'
      

      You can embed end-of-line characters directly in the editor

      
      > let haiku = "Ceaselessly we code,
      yet requirements fall
      like a gentle rain";;
      

      Use the "@" sign to denote verbatim strings, string you don't want to include escaped characters.

      > let cdir = @"C:\home\mfincher";;
      
      val cdir : string = "C:\home\mfincher"
      
      

      You can access .Net's StringBuilder class for efficient use of appending text.

      > let henry = new System.Text.StringBuilder();;
      
      val henry : System.Text.StringBuilder =
      
      > henry.Append("And gentlemen in England now-a-bed")
      henry.Append("Shall think themselves accurs'd they were not here,")
      henry.Append("And hold their manhoods cheap whiles any speaks")
      henry.Append("That fought with us upon Saint Crispin's day.")
      ;;
      
    10. Type Conversions

      F# does not do implicit type conversions for you. You, as Master of Your Fate and Captain of Your Destiny, must explicitly tell F# when you want to convert types. This hopefully leads to fewer errors. Fortunately F# provides an easy way to do that. Just preface whatever you want to convert with the desired type and viola, it's done.

      > let abyte = 200uy + 10;;
      > 
        let abyte = 200uy + 10;; //F# refuses to do the tiny conversion of "10" to a byte.
        --------------------^^
      
      stdin(67,21): error FS0001: The type 'int' does not match the type 'byte'
      let abyte = 200uy + byte 10;;
      
      val abyte : byte = 210uy
      
      > let pi = float "3.14159";; //convert a string to a float
      
      val pi : float = 3.14159
      
      > 
      
    11. Statically Typed

      F# is a "statically typed" language, meaning that at compile time it knows all the types of the variables. F# uses "type inference" to determine the type of a variable. It looks at how a variable is used and then determines the type.

    12. A final note about nothing Java and C# have the concept of "null", meaning "no object". This concept is called "unit" in F#. and written as "()". For example:
      > let x = ();;
      
      val x : unit = ()
      
      

      "void" exists in F# as well, but only for interaction with the .Net libraries.

    13. Units of Measure

      One of the great things about F# it that it allows you to annotate values with Units of Measure. (Remember the Mars Climate Orbiter burning up because all subcontractors used metric except Lockheed Martin?) F# will do limited testing to make sure your units are consistent. Units of Measure doesn't really understand the concepts of length, volume, force, or currency, but used wisely can be a great help. A little example:

      [<Measure>]
      type cm
      [<Measure>]
      type inch
      val cmPerInch : float<cm/inch> = 2.54
      
      > let length1 = 1.0<cm>
      let length2 = 1.0<inch>;;
      
      val length1 : float<cm> = 1.0
      val length2 : float<inch> = 1.0
      
      > let length3 = length1 + length2;;
      
        let length3 = length1 + length2;;
        ------------------------^^^^^^^
      
      stdin(41,25): error FS0001: The unit of measure 'inch' does not match the unit of measure 'cm'
      > let length3 = length1 + length2 * cmPerInch;;
      
      val length3 : float<cm> = 3.54
      
      
  5. Functions
    1. Our first function

      Let's define a function to square a number

      > let squareMe x = x * x;;
      
      val squareMe : int -> int
      
      > squareMe 4;;
      val it : int = 16
      

      Note that F# does not have a "return" key word; the last expression evaluated is returned.

    2. Recursive functions

      When defining a recursive function we use the keyword "rec". ( You knew we'd have to use fibonacci at least once - we'll skip Towers of Hanoi)

      let rec fib x = 
         if x <=1 then
            x
         else
            fib (x-1) + fib (x-2)
      printfn "fibonacci(%d)=%d" 10 (fib 10) //55
      
    3. Two parameters
      > let addTwo a b = a + b ;;
      
      val addTwo : int -> int -> int
      
      > addTwo 1 3;;
      val it : int = 4
      
    4. Argument Types

      In the above example, F# defaults to assuming that the argument types are ints. If we now invoke the function with floats, well, F# doesn't like that:

      > addTwo 1.0 3.0;;
      
        addTwo 1.0 3.0
        -------^^^
      
      stdin(94,8): error FS0001: This expression was expected to have type
          int    
      but here has type
          float    
      
    5. Type inference of function

      In a fascinating case of inference, if we invoke a function using a particular type before entering the ";;" F# will look at the function and then determine the types of the function. I think this is cool.

      > let subtract a b = a - b //would default to ints unless we give it a hint
      subtract 2.0 1.0 ;;
      
      val subtract : float -> float -> float
      
      
    6. Type annotation

      We can tell F# implicitly what the type of the parameters are.

      > let multiply (x: float) (y: float) = x * y ;;
      
      val multiply : float -> float -> float
      
      

      Without these annotations F# would think the function took ints.

    7. Functions within functions

      You can define functions inside other functions. These inner functions are not visible outside the containing function. (Raise your hand if you understand this.)

      > let cubeMe x = 
           let squareMe x = x * x
           squareMe x * x;;
      
      
      val cubeMe : int -> int
      
      > cubeMe 3;;
      val it : int = 27
      > squareMe 2;; //since squareMe is defined inside cubeMe, we can't see it
      
        squareMe 2
        ^^^^^^^^
      
      stdin(6,1): error FS0039: The value or constructor 'squareMe' is not defined
      > 
      

      The same is true of variables defined inside functions. They are not visible to the outside world.

  6. Statements vs. Expressions

    A "statement" does something, has no return value, and cannot be passed into a function as an argument. An "expression" is a block of code that evaluates to a value. F# has no statements.

  7. Control Flow
    1. the "if" expression

      "if" sorta works like you would expect

      > let evenOrOdd x = 
         if x % 2 = 0 then
            printfn "even"
         else
            printfn "odd";;
      
      val evenOrOdd : int -> unit
      
      > evenOrOdd 3;;
      odd
      val it : unit = ()
      > evenOrOdd 4;;
      even
      val it : unit = ()
      > 
      
    2. "if" returns a value

      Oddly enough, (no pun intended), "if" expressions return a value.

      > let evenOrOdd x = 
         let result = 
            if x % 2 = 0 then
               "even"
            else
               "odd"
       printfn "%s" result;;
      
      
      val evenOrOdd : int -> unit
      
      > evenOrOdd 5;;
      odd
      val it : unit = ()
      > 
      
    3. elif

      "elif" is a shortcut for "else if"

      > let getPrice size = 
         let price = 
           if size = "small" then 1.0f
           elif size = "medium" then 1.5f
           else 1.75f
         printfn "price=%f" price;;
      
      
      val getPrice : string -> unit
      
      > getPrice "medium";;
      price=1.500000
      val it : unit = ()
      > 
      
    4. "if"'s dark secret - it's a communist

      All paths from an "if" expression must have the same type - no commoners and elites - everyone must be equal. This stands to reason. The dirty secret about "if" is that if there is no "else" at the end of the "if", F# generates an implied "else" which has the type of ... you guessed it ... "unit". Let's see it choke on an "if" without an "else" that returns something other than "unit"

      > let getPrice size = 
         let price = 
           if size = "small" then 1.0f
           elif size = "medium" then 1.5f
         printfn "price=%f" price;;
      
      
      
           let price = 
        -----^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
      
      stdin(200,6): error FS0001: This expression was expected to have type
          float32    
      but here has type
          unit    
      > 
      

      The error above is F#'s elliptical way of telling us the implied else is returning "unit" and the rest of the "if" is returning "float32".

      Generally, unless the return type of the "if" statement is "unit", you always need an "else".

    5. Looping

      F# has "for" and "while" looping constructs, although recursive looping is the preferred iteration method.

      module Loops
      
      open System
      [<EntryPoint>]
      let main (args : string[]) =
         for i = 1 to 10 do  //prints 1..10
            printfn "i=%d" i
         for j = 10 downto 5 do //prints 10, 9, 8, 7, 6, 5
            printfn "j=%d" j
            
         let mutable x = 0
         while x < 10 do //prints 1..9
            printfn "x=%d" x
            x <- x + 1
         0
      
  8. Tuples

    Tuples are containers for two or more unnamed values of possibly different types.

    1. Here we see a tuple, pronounced "two-pull" containing four values.

      > (1,2,3.0,"four");;
      val it : int * int * float * string = (1, 2, 3.0, "four")
      

      F# shows the type of the tuple by seperating the individual types with "*", this has nothing to do with multiplication.

    2. What can be in a tuple?

      Tuples can contain simple values, expressions, and even snippets of code. Tuples are used to pass parameters into a funtion as a group and return multiple values from a function.

      > (1, 1+2,("a","b","c"), printfn "Hello");;
      Hello
      val it : int * int * (string * string * string) * unit =
        (1, 3, ("a", "b", "c"), null)
      
    3. fst, and snd

      If you have a two-element tuple you can retrieve the first value with "fst" and the second with "snd".

      > let n = (1,2);;
      
      val n : int * int = (1, 2)
      
      > fst n;;
      val it : int = 1
      > snd n;;
      val it : int = 2
      
    4. The parenthesis are optional.

      > let n = 1,2;;
      
      val n : int * int = (1, 2)
      
  9. Arrays

    Arrays are mutable, fixed-sized and zero-based sequences of the same data type.

    1. Simple Arrays

      Simple arrays are denoted by "[|", followed by the elements separated by ";" or line-feeds, ending with "|]

      > let AOlympians = [| "Aphrodite"; "Apollo"; "Ares"; "Artemis"; "Athena" |];;
      
      val AOlympians : string [] =
        [|"Aphrodite"; "Apollo"; "Ares"; "Artemis"; "Athena"|]
      
      

      You can access the elements by using the ".[n]" notation

      > let godOfWar = AOlympians.[2];;
      
      val godOfWar : string = "Ares"
      

      You can use ranges like in Ruby

       let OneToTen = [|1..10|];;
      
      val OneToTen : int [] = [|1; 2; 3; 4; 5; 6; 7; 8; 9; 10|]
      
      > let OneToTenByTwos = [|1..2..10|];;
      
      val OneToTenByTwos : int [] = [|1; 3; 5; 7; 9|]
      
      > let FiveToOne = [|5..-1..1|];;  //count down
      
      val FiveToOne : int [] = [|5; 4; 3; 2; 1|]
      
      
    2. Array.zeroCreate

      this creates an array of the given size and fills it with zero for numeric types and nulls for all others.

      If you just create the array without giving it any hints about the type, you get an error:

      > let letter = Array.zeroCreate 26;;
      
        let letter = Array.zeroCreate 26;;
        ----^^^^^^
      
      stdin(51,5): error FS0030: Value restriction. The value 'letter' has been inferred to have generic type
          val letter : '_a []    
      Either define 'letter' as a simple data term, make it a function with explicit parameters or, if you do not intend for it to be generic, add a type annotation.
      
      In the FSI you must assign a member before entering ";;" so it knows what type it will be.
       let letters = Array.zeroCreate 26
      letters.[0] <- 'a';;  //you must use the destructive assignment operator since arrays are mutable
      
      val letters : char [] =
        [|'a'; '\000'; '\000'; '\000'; '\000'; '\000'; '\000'; '\000'; '\000';
          '\000'; '\000'; '\000'; '\000'; '\000'; '\000'; '\000'; '\000'; '\000';
          '\000'; '\000'; '\000'; '\000'; '\000'; '\000'; '\000'; '\000'|]
      
      > letters.[0] = 'a';;  //if you use "=" outside a 'let' assignment F# thinks you're asking an equality question
      val it : bool = false
      
      
    3. A few examples of creating arrays:

      module arrays
      [<EntryPoint>]
      let main (args : string[]) = 
         //Array.create numberOfElements defaultValue
         let sixOnes = Array.create 6 1
         printfn "%A" sixOnes
         let tenToThirtyByFives = [|10.0; 15.0; 20.0; 25.0; 30.0|]
         printfn "%A" tenToThirtyByFives
         let tenToThirtyByFives = [| 10.0 .. 5.0 .. 30.0 |]
         printfn "%A" tenToThirtyByFives
         let tenToThirtyByFives = Array.init 5 (fun x -> float(x) * 5.0+ 10.0)
         printfn "%A" tenToThirtyByFives
         0
      

      You can specify the type of the array by any of the following:

      > let states1 : string[] = Array.zeroCreate 50;;
      > let states2 : string array = Array.zeroCreate 50;;
      > let states3 = Array.zeroCreate<string> 50;;
      
    4. Slices

      You can create new arrays from slices of an exisitng array

      > let nums = [|0..9|];;
      
      val nums : int [] = [|0; 1; 2; 3; 4; 5; 6; 7; 8; 9|]
      
      > let OneToThree = nums.[1..3];; // [|1; 2; 3|]
      > let ZeroToFive = nums.[..5];; // [|0; 1; 2; 3; 4; 5|]
      > let TwoToNine = nums.[2..];; // [|2; 3; 4; 5; 6; 7; 8; 9|]
      > let duplicate = nums.[0..];; // [|0; 1; 2; 3; 4; 5; 6; 7; 8; 9|]
      
      
    5. > let matrix = Array2D.zeroCreate<float> 3 3;;
      
      val matrix : float [,] = [[0.0; 0.0; 0.0]
                                [0.0; 0.0; 0.0]
                                [0.0; 0.0; 0.0]]
      
      > matrix.[1,2] = 2.5;; //don't do this
      val it : bool = false
      > matrix.[1,2] <- 2.5;; //use the destructive assignment operator instead
      val it : unit = ()
      

      Array3D and Array4D are available for your pleasure.

  10. Deeper into Functions
    1. Everything is a function

      Everything in F# evaluates to a value of a specific type. Even a simple value can be thought of as an expression that takes no parameters and evaluates to it's value.

      let temperature = 98.6;; //is a function
      
    2. Partial Application of Functions - things start to get weird

      Up til now everything we've learned has been pretty straight forward, a little odd at times, but not too radical, but now Alice is about to enter the rabbit hole.

      > let addTwo a b = a + b;;
      
      val addTwo : int -> int -> int
      
      > (addTwo 3) 4;;
      val it : int = 7
      > let plus3 = addTwo 3; //partial application of a function
      
      val plus3 : (int -> int)
      
      > plus3 4;;
      val it : int = 7
      

      Note that "plus3" is a function that "swallows" the "addTwo" function and its first argument, and "plus3" takes an argument that is really the second argument of the "addTwo" function.

    3. Higher Order Functions

      In functional programming functions are real objects. Higher-order functions take a function as an argument or return a function as an argument. Let's look at an example of "doTwice" a method that takes an int and a function as arguments

      module HigerOrderFunctions
      [<EntryPoint>]
      let main (args : string[]) = 
         //two tiny functions to play with later
         let addOne n = n + 1
         let divideByTwo n = n / 2
      
         // doTwice takes an integer and then 
         // a function that takes an int and returns an int
         // doTwice applies this function to the argument 
         // and then to the result of that function
         let doTwice n (f:int->int) = f(f(n))
      
         let x = doTwice 10 addOne
         printfn "x=%d" x //prints "x=12"
         let y = doTwice 12 divideByTwo
         printfn "y=%d" y //prints "y=3"
         0
      
    4. Lambda functions

      Lambdas are syntactic sugar allowing us to define and use unnamed functions. To create a lambda use the keyword "fun" followed by arguments, then "->", then the body of the function. The following is identical to the code above except we get rid of "doubleMe" and replace with a lambda function.

      module lambda
      [<EntryPoint>]
      let main (args : string[]) =
         let doTwice n (f:int->int) = f(f(n))
         let x = doTwice 10 (fun n -> n * 2) //lambda function as second argument
         printfn "x=%d" x //prints "x=40"
         0
      
      
  11. Mapping

    One of the basic concepts of functional programming in the concept of a map. A map takes one list, applies a function to each of the original list elements and creates a new list. In C# this is called "Select" and in Lisp it is "mapcar".

    F# has a built-in method on Array called "map". "map" takes two arguments: a function to apply to elements and an array of elements to operate on. The original list is untouched.

    For example, below we take an array of the numbers 1 through 10, and apply a function to each number to create a second Array.

    module Mapcar
    [<EntryPoint>]
    let main (args : string[]) = 
       let oneThruTen = [| 1 .. 10 |]
       let powersOfTwo = Array.map(fun n ->  pown 2 n ) oneThruTen
       printfn "%A" powersOfTwo //[|2; 4; 8; 16; 32; 64; 128; 256; 512; 1024|]
       0
    
    In addition to "map", Array has three other similiar methods:
    1. map2 - this takes two input arrays and creates an output array
    2. mapi - takes one input array and passes the element and its order number to the mapping function to create the output array
    3. mapi2 - takes two input arrays and passes the two elements and their order number to the mapping function to create the output array
  12. Folding

    While mapping takes a collection and creates another collection, folding takes a collection and distills it down to a single object. Array.fold takes three arguments: a function, an initial value for the accumulator, and an Array.

    module Folder
    [<EntryPoint>]
    let main (args : string[]) = 
       let oneThruTen = Array.init 10 (fun n -> n+1)
       let sum = Array.fold(fun accumulator n ->  accumulator + n ) 0 oneThruTen
       printfn "%d" sum //55
       0
    
  13. Filtering

    A filter selectively populates a new collection with some of the elements of an initial collection. Array.filter takes two arguments: a function that takes and element and returns a bool value, and an Array.

    module Filter
    [<EntryPoint>]
    let main (args : string[]) = 
        let names = [| "Katniss"; "Peeta"; "Haymitch"; "Gale"; "Primrose"; "Coriolanus";  |]
        let longNames = Array.filter (fun (name: string) -> name.Length > 6) names
        printfn "%A" longNames //[|"Katniss"; "Haymitch"; "Primrose"; "Coriolanus"|]
        0
    
  14. Zipping

    A zipping function takes to collections and combines them.

    module Zipper
    [<EntryPoint>]
    let main (args : string[]) = 
        let firstNames = [| "Katniss"; "Peeta"; "Haymitch"; |]
        let lastNames = [| "Everdeen"; "Mellark"; "Abernathy"; |]
    
        let fullNames = Array.zip(firstNames) lastNames
        printfn "%A" fullNames //[|("Katniss", "Everdeen"); ("Peeta", "Mellark"); ("Haymitch", "Abernathy")|]
        0
    
  15. Pipelining

    The pipelining operator, "|>" allows us to push arguments onto functions. The technical definition is: let inline (|>) x f = f x

    module Pipelining
    [<EntryPoint>]
    let main (args : string[]) = 
        let square a = a * a
        let four = square 2
        let four = 2 |> square
        printfn "%d" four
        let twoSquaredFourTimes = 2 |> square |> square |> square |> square 
        printfn "%d" twoSquaredFourTimes
        0
       
    
  16. Functional Composition

    When you want to pipe the output of one function into another you can use the pipelining operation shown above or you can use the composition operation, ">>". f(g(x)) is the same as h = f >> g

    module Composition
    [<EntryPoint>]
    let main (args : string[]) = 
        let square a = a * a
        let double b = b * 2
        let squareThenDouble = square >> double
        printfn "%d" (squareThenDouble 3) //18; i.e., 3 |> square |> double
        0
    
  17. Closures

    Closures are functions that have some pre-bound variables, i.e., some arguments of the function are predefined or "closed". Closures allow us to make complicated functions from many simpler ones. This encourages reuse and shorter programs.

  18. module Closure
    [<EntryPoint>]
    let main (args : string[]) = 
        let multiply x y = x * y
        let triple = multiply 3 //partial application of function.  
        // "triple" is a closure that takes one argument and multiples it by three
        printfn "%d" (triple 5) //15
        0
    
  19. Lazy Evaluation

    You can delay evaluation by using Lazy evaluation. The expression will only evaluate when "Force" is invoked and the value is cached, (or memoized) and returned for future invocations of "Force". In the code below note that "multiplying 7 by 6" is printed only once, even through we invoke it twice.

    module Lazy
    [<EntryPoint>]
    let main (args : string[]) = 
        let multiply x y = 
            printfn "multiplying %d by %d" x y
            x * y
        let answer = lazy(multiply 7 6)
        printfn "after assignment."
        printfn "%d" (answer.Force()) //prints 'multiplying 7 by 6' '42'
        printfn "%d" (answer.Force()) //prints only '42' since calculation was memoized.
        0
    

    Produces:

    after assignment.
    multiplying 7 by 6
    42
    42
    
  20. Operator Overloading

    The following symbols can be used for operators: !, $, %, &, *, +, -, ., /, <, =, >, ?, @, ^, | and ~. You define the operator like this:

    let (operator-symbols) parameter-list = 
        function-body
    

    module Overload
    [<EntryPoint>]
    let main (args : string[]) = 
        let ($) (x: float) (y: float) = (x + y)/2.0
        let avg = 10.0 $ 20.0
        printfn "%f" avg   //15.000000
        0
    

    You are not limited to a single symbol, but can use a seqence of the symbols to make new operators

    module Overload
    [<EntryPoint>]
    let main (args : string[]) = 
        //define geometric mean as the awkward sequence "%!"
        let (%!) (x: float) (y: float) = System.Math.Sqrt(x * y)
        let geoMean = 2.0 %! 10.0
        printfn "%f" geoMean  //4.472136
        0
    

    So far all of our operators have been binary, taking two arguments with our operator in the middle. You can define unary operators by prefixing the definition with "~".

    module Overload
    [<EntryPoint>]
    let main (args : string[]) = 
        let (~%) (x: float) = System.Math.Round(x)
        let num = % 10.2
        printfn "%f" num //10.0
        0
    
Go to Home page. Kindly report errors, typos, or misspellings here.