Fundamentals of Delegates

Introduction

A delegate is an object that is created to act as, or in place of, a function. The delegate is primarily created as a type but with the signature of a function. This means that the function or object is not defined. It is only meant to represent a function that can be eventually called. Then, when necessary, the object as function would be called as a function.

Creating a Delegate

The primary formula to create a delegate is:

type delegate-name = delegate of parameter-type -> return-type

You start with the required and common type keyword used to create types in F#. This is followed by a name for the delegate, which follows the rules for names of classes.

Besides the type keyword, the delegate name is followed by the = delegate of expression, which is required. If the function will not take a parameter, specify its parameter-type as unit. To complete the signature, you must add -> and tne return type of the function. If the function will not return a specific value, specify its return-type as unit. Here is an example:

type TitleCreation = delegate of unit -> unit

An F# Function Class

The F# language supports delegates through an abstract class named FSharpFunc:

[<AbstractClass>]
type FSharpFunc<'T,'U> =
    class
        new FSharpFunc : unit -> FSharpFunc<'T,'U>
        static member FromConverter : Converter<'T,'U> -> 'T -> 'U
        abstract this.Invoke : 'T -> 'U
        static member InvokeFast : FSharpFunc<'T,('U -> 'V)> * 'T * 'U -> 'V
        static member InvokeFast : FSharpFunc<'T,('U -> 'V -> 'W)> * 'T * 'U * 'V -> 'W
        static member InvokeFast : FSharpFunc<'T,('U -> 'V -> 'W -> 'X)> * 'T * 'U * 'V * 'W -> 'X
        static member InvokeFast : FSharpFunc<'T,('U -> 'V -> 'W -> 'X -> 'Y)> * 'T * 'U * 'V * 'W * 'X -> 'Y
        static member ToConverter : ('T -> 'U) -> Converter<'T,'U>
        static member op_Implicit : Converter<'T,'U> -> 'T -> 'U
        static member op_Implicit : ('T -> 'U) -> Converter<'T,'U>
    end

The class is defined in the Microsoft.FSharp.Core namespace (https://github.com/dotnet/fsharp/blob/main/src/FSharp.Core/prim-types.fs) of the FSharp.Core.dll library. This means that you will hardly, if ever, need to use it in your programs. Instead, when you create a delegate, it directly becomes a class that is derived from FSharpFunc. This means that the members of the FSharpFunc class become direct members of your delegate.

When you create a delegate, it becomes an object (or a function object) of type FSharpFunc. The FSharpFunc class has a constructor and methods.

Associating a Function to a Delegate

As mentioned already, a delegate only represents the signature of a function. To use a delegate, you must associate it to a function. If you don't have one already, you can start by creating a function. Here is an example of a function:

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

As mentioned above, a delegate is an object of the FSharpFunc type. This class has a constructor. To associate a function to a delegate, declare a variable and initialize it with the constructor. Based on this, the primary formula to associate a function to a delegate is:

let variable-name = new delegate-name(function-name)

This is equivalent to declaring a variable for a type, as done for a class, using the name as constructor. In the parentheses of the contructor, type the name of the function as parameter. This can be done as follows:

type TitleCreation = delegate of unit -> unit

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

let title = TitleCreation(getTitle)

You can optionally use the new operator when initializing the variable. Here is an example:

type TitleCreation = delegate of unit -> unit

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

let title = new TitleCreation(getTitle)

The Type of a Delegate

Remember that, when declaring a variable, you can indicate the type of the variable. To do this when associating a function to a delegate, the formula to follow is:

let variable-name : delegate-name = new delegate-name(function-name)

Here is an example of specifying the type of the variable:

type TitleCreation = delegate of unit -> unit

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

let title : TitleCreation = new TitleCreation(getTitle)

Invoking a Delegate

After associating the function to the delegate, you can use the variable as if it were a function. To let you do this, the FSharpFunc class that each delegate derives from, is equipped with a method named Invoke. All you have to do is to call this method to the delegate variable. Here is an example:

type TitleCreation = delegate of unit -> unit

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

let title : TitleCreation = new TitleCreation(getTitle)

title.Invoke()

This would produce:

Introduction to F# Programming, Second Edition
Press any key to close this window . . .

A Delegate that Returns a Value

A delegate presents the syntax of a function. As such, it can indicate whether its function(s) must return a value. That's the role of the last part of the formula we saw:

type delegate-name = delegate of parameter(s) type(s) -> return-type

Here is an example of a delegate that takes no parameter and returns a string:

type Initialyze = delegate of unit -> string

Remember that any function that will be associated with such a delegate must return the same type of value of that delegate. Here is an example:

type Initialyze = delegate of unit -> string

let initialyzer() = "Enterprise Database Design"

You associate the function to the delegate the same way we have done so far. Here is an example:

type Initialyze = delegate of unit -> string

let initialyzer() = "Enterprise Database Design"

let result = new Initialyze(initialyzer)

If you have a delegate that returns a value, when calling its function, you can use the value however you judge necessary, as long as you keep in mind the type of value the function is returning. Here is an example:

type Initialyze = delegate of unit -> string

let initialyzer() = "Enterprise Database Design"

let result = Initialyze(initialyzer)

printfn "Course Title: %s" (result.Invoke())

This would produce:

Course Title: Enterprise Database Design
Press any key to close this window . . .

In the same way, you can use a delegate that takes one or more arguments and returns a value.

Delegates and Parameters

Introduction to Passing an Argument to a Delegate

As a delegate simply presents the layout of a function, it can use a parameter. Remember that the basic formula to create a delegate is:

type delegate-name = delegate of parameter-type -> return-type

In this case, in the signature of the delegate, only specify the data type of the parameter. Here is an example of a delegate that takes a string as parameter and returns no value:

type CourseDescription = delegate of string -> unit

Any function that will use this delegate must take the same type of parameter. Here is an example:

let describe str =
    printfn "Course Name: %s" str

To associate the function to the delegate, you declare a let variable as we have done so far. Initialyze it using the constructor of the delegate. Here is an example:

type CourseDescription = delegate of string -> unit

let describe str =
    printfn "Course Name: %s" str

let desc = new CourseDescription(describe)

To support parameters, the FSharpFunc class provides many options on its Invoke() method. This includes the ability to pass one or more parameters. As a result, when calling Invoke, pass an argument that is the same type that was specified when creating the delegate and when defining the function. Here is an example:

type CourseDescription = delegate of string -> unit

let describe str =
    printfn "Course Name: %s" str

let desc = CourseDescription(describe)

desc.Invoke("Enterprise Database Design")

You can omit the parentheses on the Invoke():

type CourseDescription = delegate of string -> unit

let describe str =
    printfn "Course Name: %s" str

let desc = new CourseDescription(describe)

desc.Invoke "Enterprise Database Design"

This would produce:

Course Name: Enterprise Database Design
Press any key to close this window . . .

A Delegate With Many Parameters

To indicate what its prototyped function(s) would look like, a delegate can take as many parameters as necessary. The formula to create such a delegate is:

type delegate-name = delegate of type_1 * type_2 * ... * type_n -> return-type

In the placeholder of the parameter, specify each type and separate them with *. Here is an example of a delegate for (a) function(s) that take(s) a string followed by an integer and returns a string:

type Initialyze = delegate of string * int -> string

Of course, when defining the function(s), make sure you use the same number of parameters and return the same type of value. Here is an example:

let initialyzer courseName credits =
    courseName + ", " + (string credits) + " credits"

To associate the function to the delegate, you proceed as we have done so far. Here is an example:

type Initialyze = delegate of string * int -> string

let initialyzer courseName credits =
    courseName + ", " + (string credits) + " credits"

let result = new Initialyze(initialyzer)

To let you use the variable, remember that the FSharpFunc.Invoke() method allows you to pass as many arguments as you need. Make sure you pass the same number of arguments and types specified in both the delegate and the function. Here is an example:

type Initialyze = delegate of string * int -> string

let initialyzer courseName credits =
    courseName + ", " + (string credits) + " credits"

let result = Initialyze(initialyzer)

let value = result.Invoke("Enterprise Database Design", 3)

printfn "Course Title: %s" value

This would produce:

Course Title: Enterprise Database Design, 3 credits
Press any key to close this window . . .

In the above example, we first declared a variable to store the value of calling the Invoke() method. This is not always necessary. Instead, you can call the method directly where it is needed. Here is an example:

type Initialyze = delegate of string * int -> string

let initialyzer courseName credits =
    courseName + ", " + (string credits) + " credits"

let result = Initialyze(initialyzer)

printfn "Course Title: %s" (result.Invoke("Enterprise Database Design", 3))

A Delegate as a Parameter

Since a delegate is a type (like a class or a data type), it can be passed as argument to another delegate or to a function. To specify that a parameter is a delegate type, when creating the delegate, apply the name of a delegate. Of course, you must have a delegate. You can first create one and then use it. Here is an example of a delegate named Catalogue that takes a parameter that is of a type of delegate:

type Initialyzer = delegate of string * int -> string // First Delegate
type Catalogue = delegate of Initialyzer -> unit // Second Delegate

In the same way, you can pass a combination of (a) delegate(s) and other types of parameters passed to a delegate. Here is an example of a delegate that takes a delegate, two strings, and an integer as parameters. The delegate returns a string:

type Cataloguer = delegate of Initialyzer * string * string * int -> string

When creating the function(s), pass the same number and types of arguments. Make sure you pass the first delegate in the same placeholder. In the body of the function, declare a variable and initialyze it with a function that implements the first delegate. You have two options. If you will need the first function only inside the new function, and because the F# language allows it, you can define the first function inside the new function. Here is an example:

type Initialyzer = delegate of string * int -> string
type Cataloguer = delegate of Initialyzer * string * string * int -> string

let createCatalog (init : Initialyzer) name title credits =
    let initialyze courseName credits =
        courseName + "(" + (string credits) + ")"
    let course =  new Initialyzer(initialyze)

Otherwise, if you will use the first function globally, you should define it outside the new function. Somewhere in the function, you can call the Invoke() method to get the value of the variable. Before exiting the function, if necessary, make sure you return the appropriate value indicated by both the delegate and the function. Here is an example:

type Initialyzer = delegate of string * int -> string
type Cataloguer = delegate of Initialyzer * string * string * int -> string

let createCatalog (init : Initialyzer) name title credits =
    let initialyze courseName credits =
        courseName + "(" + (string credits) + ")"
    let course =  new Initialyzer(initialyze)
    name + ": " + course.Invoke(title, credits)

When calling the Invoke() method on the function variable associated with the second delegate, make sure you pass an argument in the appropriate placeholder of the delegate parameter. Here is an example:

type Initialyzer = delegate of string * int -> string
type Cataloguer = delegate of Initialyzer * string * string * int -> string

let initialyze courseName credits =
    courseName + "(" + (string credits) + ")"

let createCatalog (init : Initialyzer) name title credits =
    let course =  Initialyzer(initialyze)
    name + ": " + course.Invoke(title, credits)

let catalogue = Cataloguer(createCatalog)
let init = Initialyzer(initialyze)
let result = catalogue.Invoke(init, "CMIS 226", "Enterprise Database Design", 3)

printfn "Course Decription: %s" result

This would produce:

Course Decription: CMIS 226: Enterprise Database Design(3)
Press any key to close this window . . .

Remember that if you are not planing to use the delegate value many times, you don't have to first declare a variable for it. You can write this:

type Initialyzer = delegate of string * int -> string
type Cataloguer = delegate of Initialyzer * string * string * int -> string

let initialyze courseName credits =
    courseName + "(" + (string credits) + ")"

let createCatalog (init : Initialyzer) name title credits =
    name + ": " + (new Initialyzer(initialyze)).Invoke(title, credits)

let catalogue = new Cataloguer(createCatalog)

let result = catalogue.Invoke(new Initialyzer(initialyze), "CMIS 226", "Enterprise Database Design", 3)

printfn "Course Decription: %s" result

Or this:

type Initialyzer = delegate of string * int -> string
type Cataloguer = delegate of Initialyzer * string * string * int -> string

let initialyze courseName credits =
    courseName + "(" + (string credits) + ")"

let createCatalog (init : Initialyzer) name title credits =
    name + ": " + (new Initialyzer(initialyze)).Invoke(title, credits)

let result = (new Cataloguer(createCatalog)).Invoke(new Initialyzer(initialyze), "CMIS 226", "Enterprise Database Design", 3)

printfn "Course Decription: %s" result

Or even this:

type Initialyzer = delegate of string * int -> string
type Cataloguer = delegate of Initialyzer * string * string * int -> string

let initialyze courseName credits =
    courseName + "(" + (string credits) + ")"

let createCatalog (init : Initialyzer) name title credits =
    name + ": " + (new Initialyzer(initialyze)).Invoke(title, credits)

printfn "Course Decription: %s" ((new Cataloguer(createCatalog)).Invoke(new Initialyzer(initialyze), "CMIS 226", "Enterprise Database Design", 3))

Delegates and Classes

Delegates and Methods

When you create a delegate, you don't specify that only functions can associate to it. A method can as well be associated to a delegate. The main difference is that you may have to create an object (declare a variable of a class) from which you would call the method. Of course, any method can be used as long as there is an appropriate delegate to which it can be associated. Other than that, everything else follows the rules we reviewed for functions:

Delegates and Static Members of a Class

So far, we were declaring a variable to use a class outside its body. If you create a static method in a class and you want to associate that method to a delegate, remember that you don't have to declare an instance of the class to access that method. Here is an example:

type Calculation = delegate of bool * float -> float

type EmploymentStatus =
    | Unknown  = 0
    | FullTime = 1
    | PartTime = 2
    | Intern   = 3
    | Seasonal = 4

type Employee() =
    static let mutable emplNbr = ""
    static let mutable fn = ""
    static let mutable ln = ""
    static let mutable status = EmploymentStatus.Unknown
    static let mutable sal  = 0.00
    static member EmployeeNumber with get() = emplNbr and set(value) = emplNbr <- value
    static member FirstName with get() = fn and set(value) = fn <- value
    static member LastName with get() = ln and set(value) = ln <- value
    static member Status with get() = status and set(value) = status <- value
    static member HourlySalary with get() = sal and set(value) = sal <- value
    
    static member CalculateSalary salaryIsFixed timeWorked =
        if salaryIsFixed = true then
            Employee.HourlySalary * 40.00
        else
            Employee.HourlySalary * timeWorked
    
Employee.EmployeeNumber <- "8485-2082"
Employee.FirstName <- "Joseph"
Employee.LastName <- "Almunia"
Employee.Status <- EmploymentStatus.FullTime
Employee.HourlySalary <- 32.25

let result = Calculation(Employee.CalculateSalary)

printfn "Employee Payroll Summary"
printfn "----------------------------------------------------"
printfn "Employee #:    %s" Employee.EmployeeNumber
printfn "Full Name:     %s %s" Employee.FirstName Employee.LastName
printfn "Status:        %A" Employee.Status
printfn "Hourly Salary: %0.02F" Employee.HourlySalary
printfn "----------------------------------------------------"
printfn "Weekly Salary: %0.02F" (result.Invoke(true, 48.00))

This would produce:

Employee Payroll Summary
----------------------------------------------------
Employee #:    8485-2082
Full Name:     Joseph Almunia
Status:        FullTime
Hourly Salary: 32.25
----------------------------------------------------
Weekly Salary: 1290.00
Press any key to close this window . . .

An Object as a Parameter to a Delegate

As we saw already, a delegate can take a parameter and/or return a value. The parameter of a delegate can be a primitive type or a class. Here is an example:

type Description = delegate of BasketballCourt -> unit

This an example of a delegate for a function that would take a parameter from a class named BasketballCourt. Of course, you must use a type of object. You can use an existing class or you can create your own. When defining the function, you must pass an object of the same type passed to the delegate. The rest is done as we saw so far. Here is an example:

type Rectangle() =
    let mutable len = 0.00
    let mutable wd  = 0.00
    member this.Length with get() = len and set(value) = len <- value
    member this.Width with get() = wd and set(value) = wd <- value
    member this.Perimeter with get() = (len + wd) * 2.00
    member this.Area with get() = len * wd
    
type Circle() =
    let mutable radius = 0.00
    member me.Radius with get() = radius and set(value) = radius <- value
    member me.Diameter with get() = radius * 2.00
    member me.Circumference with get() = me.Diameter * 3.14156
    member me.Area with get() = radius * radius * 3.14156

type BasketballCourt() =
    let mutable external = new Rectangle()
    let mutable central  = new Circle()
    let mutable rar = 0.00
    let mutable rh = 0.00
    let mutable kw = 0.00
    let mutable ftld = 0.00
    let mutable b3pld = 0.00
    member sport.Court with get() = external and set(value) = external <- value
    member sport.Center with get() = central and set(value) = central <- value
    member sport.RestrictedArcRadius with get() = rar and set(value) = rar <- value
    member sport.RimHeight with get() = rh and set(value) = rh <- value
    member sport.KeyWidth with get() = kw and set(value) = kw <- value
    member sport.FreeThrowLineDistance with get() = ftld and set(value) = ftld <- value
    member sport.B3PointLineDistanceFromBasket with get() = b3pld and set(value) = b3pld <- value
   
let NBACourt = BasketballCourt()
let NBA = Rectangle()
NBA.Length <- 28.65
NBA.Width <- 15.24
NBACourt.Court <- NBA
NBACourt.RimHeight <- 3.05;
NBACourt.RestrictedArcRadius <- 1.22;

let courtCenter = Circle()
courtCenter.Radius <- 1.22;
NBACourt.Center <- courtCenter
NBACourt.B3PointLineDistanceFromBasket <- 1.22;
NBACourt.KeyWidth <- 4.88;
NBACourt.FreeThrowLineDistance <- 4.57;

type Description = delegate of BasketballCourt -> unit

let describe (court : BasketballCourt) =
    printfn "Basketball Court"
    printfn "_____________________________________________________"
    printfn "Court Measures ______________________________________"
    printfn "   Length:             %0.02f m" court.Court.Length
    printfn "   Width:              %0.02f m" court.Court.Width
    printfn "   Perimeter:          %0.02f m" court.Court.Perimeter
    printfn "   Area:               %0.02f m" court.Court.Area
    printfn "Central Circle Measures ____________________________"
    printfn "   Radius:            %0.02f m" court.Center.Radius
    printfn "   Diameter:          %0.02f m" court.Center.Diameter
    printfn "   Circumference:     %0.02f m" court.Center.Circumference
    printfn "   Area:              %0.02f m" court.Center.Area
    printfn "____________________________________________________"
    printfn "Restricted Arc Radius: %0.02f m" court.RestrictedArcRadius
    printfn "3-Point Line Distance From Basket:  %0.02f m" court.B3PointLineDistanceFromBasket
    printfn "Key Width (Shaded Lane or Restricted Area: %0.02f m" court.KeyWidth
    printfn "Free-throw line distance from point on the floor directly below the backboard: %0.02f m" court.FreeThrowLineDistance
    printfn "____________________________________________________"

let result = Description(describe)

result.Invoke NBACourt

This would produce:

Basketball Court
_____________________________________________________
Court Measures ______________________________________
   Length:             28.65 m
   Width:              15.24 m
   Perimeter:          87.78 m
   Area:               436.63 m
Central Circle Measures ____________________________
   Radius:            1.22 m
   Diameter:          2.44 m
   Circumference:     7.67 m
   Area:              4.68 m
____________________________________________________
Restricted Arc Radius: 1.22 m
3-Point Line Distance From Basket:  1.22 m
Key Width (Shaded Lane or Restricted Area: 4.88 m
Free-throw line distance from point on the floor directly below the backboard: 4
.57 m
____________________________________________________
Press any key to close this window . . .

Although we passed a parameter based on a class, the parameter can actually be of any valid type, such as a record. Here is an example:

type RoadType =
    | Court
    | Street
    | Road
    | Avenue
    | Boulevard
    | StateRoad
    | StateHighway
    | Interstate
    | Unknown
    
type Road = {
    Name : string
    Category : RoadType
    Distance : float
    Location  : string }

let USRoute1 =
    {
        Name = "U.S. Route 1";
        Category = RoadType.Road;
        Distance = 2369.00;
        Location = "From FortKent, Maine To Key West, Florida";
    }

type RoadDescription = delegate of Road -> unit

let describe (rd : Road) =
    printfn "Road System Database"
    printfn "=----------------------------------------------------="
    printfn "Road Name: %s" rd.Name
    printfn "Category:  %A" rd.Category
    printfn "Distance:  %0.2f miles" rd.Distance
    printfn "Location:  %s\n" rd.Location

let desc = RoadDescription(describe)

desc.Invoke USRoute1

This would produce:

Road System Database
=----------------------------------------------------=
Road Name: U.S. Route 1
Category:  Road
Distance:  2369.00 miles
Location:  From FortKent, Maine To Key West, Florida

Press any key to close this window . . .

As mentioned for functions, you can pass a combination of parameters of various types such as a class and other classes, classes and objects, or classes and primitive types.

A Delegate that Returns an Object

As seen already, a delegate can return a value, and that value can be a type of object (class, record, etc). The type can be an existing class or you can create yor own. Of course, the associated function(s) must also return the same type of object. Everything else is done as we have done so far. Here is an example:

type Employee() =
    let mutable emplNbr = ""
    let mutable fn = ""
    let mutable ln = ""
    let mutable sal  = 0.00
    member this.EmployeeNumber with get() = emplNbr and set(value) = emplNbr <- value
    member this.FirstName with get() = fn and set(value) = fn <- value
    member this.LastName with get() = ln and set(value) = ln <- value
    member this.HourlySalary with get() = sal and set(value) = sal <- value
    
type PayrollPresentation = delegate of unit -> Employee

let createEmployeeRecord() =
    let empl : Employee = Employee()
    empl.EmployeeNumber <- "2080-4813"
    empl.FirstName <- "James"
    empl.LastName <- "Alexanders"
    empl.HourlySalary <- 22.25
    empl

let presentation = new PayrollPresentation(createEmployeeRecord)

let empl = presentation.Invoke()

printfn "Employee Payroll Summary"
printfn "----------------------------------------------------"
printfn "Employee #:    %s" empl.EmployeeNumber
printfn "Full Name:     %s %s" empl.FirstName empl.LastName
printfn "Hourly Salary: %0.02F" empl.HourlySalary
printfn "----------------------------------------------------"

This would produce:

Employee Payroll Summary
----------------------------------------------------
Employee #:    2080-4813
Full Name:     James Alexanders
Hourly Salary: 22.25
----------------------------------------------------
Press any key to close this window . . .

In the same way, you can create a delegate that takes any type of value of your choice and returns an object of a class type.

F# Support for Function Objects

Introduction

Instead of making you create your own delegates from scratch, the F# language provides its own and strong support for function objects. It does this in the concepts of function values and lambda expressions.

A Function as Parameter

By definition, a function is a value by itself. This means that one function can be directly defined in the body of another function. Such a function is passed as a parameter and it is called a function value.

To pass a function as parameter, after the name of the function that takes it as argument but before its own argument(s), open some parentheses. In the parentheses, specify the signature of the function parameter: a name, followed by a column, the data type(s) of its argument(s), the -> operator, and the return type. Here is an example:

let display (show : unit -> unit) = . . .

In this case, we are creating a function named display. That primary function takes a parameter named show. This parameter is treated as a function. That function will take no parameter and will not return anything.

In the body of the primary function, you can use the parameter as a function. At a minimum, you can simply call it as a function. Here is an example:

let display (show : unit -> unit) =
    show()

Before calling the primary function, you must define the function, or you must define a function, that performs the task of the function value. Here is an example:

let display (show : unit -> unit) = show()
let present msg =
    printfn "Welcome to the wonderful world of functional programming!"

When calling the primary function, pass the second function as argument in the placeholder of the function passed as parameter. Here is an example:

let display1 (show : unit -> unit) = show()
let present1 msg = printfn "Welcome to the wonderful world of functional programming!"

display present

This would produce:

Welcome to the wonderful world of functional programming!

If the function parameter must take a parameter, specify its type in the signature of the parameter, on the left side of the -> operator. Here is an example:

let display (show : string -> unit) = . . .

In most cases, you should pass another parameter to the primary function. That other parameter should (must) be the same type as the parameter of the function argument. In the body of the primary function, you can call the function parameter as a function. If you had passed an additional parameter to the primary function, you can pass it to the function parameter. Here is an example:

let display (show : string -> unit) message = show message;

Remember that you should always have a secondary function that actually implements the function parameter. The second function must take the same number of parameters and types as the function parameter. Here is an example:

let display (show : string -> unit) message = show message;

let present msg = printfn "Message: %s" msg;

When calling the primary function, remember to pass a secondary function as argument. Remember that you must pass an argument to that parameter. Here is an example:

let display (show : string -> unit) message = show message;
let present msg = printfn "Message: %s" msg;

display present "Welcome to the wonderful world of functional programming!"

Instead of returning unit as we have done so far, you can make the function parameter return any value of your choice. Other than that, you must follow the necessary rules of functions. Here is an example of a function parameter that takes a string and returns a string:

let display (show : string -> string) message = show message;
let present msg = msg;

let result = display present "Welcome to the wonderful world of functional programming!";
printfn "Message: %s" result

This would produce:

Message: Welcome to the wonderful world of functional programming!

You can pass as many parameters as you want to the primary function. In its body, use those additional parameters as you see fit. Here is an example of a function that takes three normal parameters in addition to a function parameter:

let createCatalog (description : string -> string) name teacher period =
    "Teacher:\t" + teacher + "\n" + description name + "\nSchedule:\t" +  period;

let describe crsName = "Course Name: " + crsName;

let catalog = createCatalog describe "Introduction to Library Research Skills" " Dr. Emmanuelle Critenden" " 02/12/2015 - 04/06/2015";
printfn "School Catalog\n-----------------------------------------------------"
printfn "%s" catalog

This would produce:

School Catalog
---------------------------------------------------
Teacher:	 Dr. Emmanuelle Crittenden
Course Name: Introduction to Library Research Skills
Schedule:	 02/12/2015 - 04/06/2015

You can pass as many function parameters as you want to a primary function. For any function parameter that is not void, you should pass an additional parameter to the primary function, to be used by the function parameter. In the body of the primary function, each function parameter can be used as a normal function that is being called.

Introduction to Lambda Expressions

Imagine you have a relatively small function such as one that calculates the square of a number. Here is such a function:

let squareIt a = a * a

An anonymous function, also called a function expression, is a type of function that is passed to another function as argument, as we saw about function parameters. This time, to create the function you are passing as argument, that is, to create the lambda expression, use the following formula:

fun | function parameter(s) -> expression

The expression starts with the fun or the function keyword. The function is not named. Instead, the fun or function keyword is followed by the name(s) of the parameter(s) of the function/expression. After the name(s) of the parameter(s), type the -> operator and the purpose or definition of the function. Here is an example:

fun x -> x * x

You pass this whole definition to the function that receives it. Here is an example:

calculate (fun e -> e * e) 12

You must (have) define(d) a function that specifies what the parent or primary function does. In that primary function, the lambda expression is represented by a name. That name is usually set as pred, which stands for predicate. Here is an example where a function takes one function as parameter and another value as another parameter. The primary function doubles the value of the parameter and passes the value to the lambda expression:

let calculate predicate number =
    let doubled = number * 2
    predicate doubled;

Remember that if you want, in some cases, you can retrieve the return value of the function and store it in a variable. Here is an example:

let calculate predicate number =
    let doubled = number * 2
    predicate doubled;

let result = calculate (fun e -> e * e) 12
printfn "Number = %d" result;

This would produce:

Number = 576
Press any key to close this window . . .

In some cases, the parameter passed to fun or function is an object of a class type. In this case, you should (must) specify the type of the parameter. To do this, put the parameter name and iTS type in parentheses separated by :.

In some cases, if you are not interested in the return value of the function, you can instruct the compiler to ignore it.

As seen previously, an alternative to passing a function as argument is to use a lambda expression. Here are examples from a function:

let solveEquation pred a b c =
    printf "In solving "
    if a = -1 then
        printf "-x"
    elif a = 1 then
        printf "x"
    else
        printf "%dx" a;
    if b < -1 then
        printf "%d" b
    elif b = -1 then
        printf " - %d" (-1 * b)
    elif b = 0 then
        printf ""
    else // b > 0 then
        printf "+%d" b
    
    printf "=%d, " c
    
    printfn "we get x = %d" (pred a b c);

solveEquation (fun a b c -> (c - b) / a) 1 50 120
solveEquation (fun a b c -> (c - b) / a) 1 -5 7
solveEquation (fun a b c -> (c - b) / a) 4 0 8
solveEquation (fun a b c -> (c - b) / a) 1 -4 2
solveEquation (fun a b c -> (c - b) / a) 1 8 72
solveEquation (fun a b c -> (c - b) / a) 5 0 10

These would produce

In solving x+50=120, we get x = 70
In solving x-5=7, we get x = 12
In solving 4x=8, we get x = 2
In solving x-4=2, we get x = 6
In solving x+8=72, we get x = 64
In solving 5x=10, we get x = 2

Press any key to close this window . . .

Active Patterns and Lambda Expressions

We had an introduction to active patterns. When it comes to active patterns, instead of passing an argument to the function, you can create a lambda expression in the body of the function. That lambda expression can take an argument. Here is an example:

let (|Calculate|) = fun x -> x * x * 3.14

Of course, you can specify the data type of the argument. Here is an example:

let (|Calculate|) = fun (x : float) -> x * x * 3.14

When calling the function, pass the desired value as argument. Here is an example:

let (|Calculate|) = fun (x : float) -> x * x * 3.14

let area = (|Calculate|) 25.50

printfn "Area of Circle: %0.04f" area

If necessary, the lambda expression can take as many arguments as you want. Here is an example:

let (|Calculate|) = fun x y -> x * y

let length = 48.79
let height = 33.63
let area = (|Calculate|) length height

printfn "Rectangle Characteristics"
printfn "----------------------"
printfn "Length: %0.02f" length
printfn "Height: %0.02f" height
printfn "Area:   %0.04f" area

In the same way, the function can be called in a matching pattern with the right arguments. Here is an example:

let (|Calculate|) = fun x y -> x * y

let length = 48.79
let height = 33.63
let area = (|Calculate|) length height

match (|Calculate|) length height with
| _ -> printfn "Rectangle Characteristics"
       printfn "----------------------"
       printfn "Length: %0.02f" length
       printfn "Height: %0.02f" height
       printfn "Area:   %0.04f" area

Actions Delegates

Introduction

Although all or most .NET languages support delegates and lambda expressions, the .NET Framework provides its own support for delegates. It does this through various classes. Therefore, instead of making you create delegates from scratch, and to provide universal support for all .NET languages, the .NET Framework provides various pre-built or built-in delegates that you can use directly in your programs.

Creating a Simple Action

One of the built-in delegates of the .NET Framework is named Action. It is provided in various syntaxes. The Action delegate is defined in the System namespace of the mscorlib.dll library. You should include that namespace in the document of your program.

The primary type of a Action delegate you will use is for functions (called procedures in some other languages) that take no parameter and return nothing. Its signature is:

type Action = 
    delegate of unit -> unit

Notice that the signature exactly resembles the one we saw in our introduction to delegates. This time, the name of the delegate is replaced with Action. Also notice that the delegate of expression is used, exactly as it was in our introduction to delegates. This means that an Action delegate is of type FSharpFunc. This also means that such a delegate receives all the members of that class, including the Invoke() method.

To create an action for the delegate, declare a variable. Since you would not have created a delegate, use Action to initialize the variable and add its parentheses. The rest is done as we saw already. This means that you should first create a function, or define a method, and pass it to the parentheses of Action.

Consider the simple example we saw in our introduction:

type TitleCreation = delegate of unit -> unit

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

let title = TitleCreation(getTitle)

title.Invoke()

This would produce:

Introduction to F# Programming, Second Edition
Press any key to close this window . . .

To use the Action deleegate, include the System namespace in your code file and replace the name of the delegate with Action. Here is an example:

open System

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

let title = Action(getTitle)

title.Invoke()

If you want, you can include the new operator before Action. Here is an example:

open System

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

let title = new Action(getTitle)

title.Invoke()

If you want to specify the data type of the variable, type : Action after the name of the variable. Here is an example:

open System

let getTitle() : unit =
    printfn "Introduction to F# Programming, Second Edition"

let title : Action = new Action(getTitle)

title.Invoke()

An Action Delegate With Parameters

To support parameters passed to a delegate, the Action delegate is provided in many versions, each for a certain number of parameters. The primary version in this category uses the following signature:

type Action = 
    delegate of  
        obj:'T -> unit

This syntax is for a procedure (function) that takes one parameter and returns nothing. As you can see, the parameter is a generic type, meaning it can be any type such as a primitive type. When calling Action, you must specify the type of the parameter. To do this, after Action, add the <> operator. Inside, enter the type of the parameter. Here is an example:

Action<string>()

Everything else is done as seen so far. As mentioned already, you can use the Action delegate that takes one parameter and the parameter can be a primitive type. Here is an example:

open System

let describe str =
    printfn "Course Name: %s" str

let desc = new Action<string>(describe)

desc.Invoke "Enterprise Database Design"

This would produce:

Course Name: Enterprise Database Design
Press any key to close this window . . .

The parameter can be a class type. Here is an example:

open System

type Circle() =
    let mutable radius = 0.00
    member me.Radius with get() = radius and set(value) = radius <- value
    member this.Diameter with get() = radius * 2.00
    member this.Circumference with get() = this.Diameter * 3.14156
    member this.Area with get() = radius * radius * 3.14156

let describe (circle : Circle) =
    printfn "Circle Characteristics"
    printfn "---------------------------"
    printfn "Radius:        %0.02f" circle.Radius
    printfn "Diameter:      %0.02f" circle.Diameter
    printfn "Circumference: %f" circle.Circumference
    printfn "Area:          %f\n" circle.Area
    
let round = Circle()
round.Radius <- 48.06

let result = Action<Circle>(describe)

result.Invoke round

This would produce:

Circle Characteristics
---------------------------
Radius:        48.06
Diameter:      96.12
Circumference: 301.966747
Area:          7256.260935

Press any key to close this window . . .

The parameter of an action can be another type such as a record. Here is an example:

open System

type RoadType =
    | Court
    | Street
    | Road
    | Avenue
    | Boulevard
    | StateRoad
    | StateHighway
    | Interstate
    | Unknown
    
type Road = {
    Name : string
    Category : RoadType
    Distance : float
    Location  : string }

let USRoute1 =
    {
        Name = "U.S. Route 1";
        Category = RoadType.Road;
        Distance = 2369.00;
        Location = "From FortKent, Maine To Key West, Florida";
    }

let describe (rd : Road) =
    printfn "Road System Database"
    printfn "=----------------------------------------------------="
    printfn "Road Name: %s" rd.Name
    printfn "Category:  %A" rd.Category
    printfn "Distance:  %0.2f miles" rd.Distance
    printfn "Location:  %s\n" rd.Location

let desc = Action<Road>(describe)

desc.Invoke USRoute1

This would produce:

Road System Database
=----------------------------------------------------=
Road Name: U.S. Route 1
Category:  Road
Distance:  2369.00 miles
Location:  From FortKent, Maine To Key West, Florida

Press any key to close this window . . .

The method of a class can be passed to an Action delegate. Here is an example:

open System

type EmploymentStatus =
    | Unknown  = 0    
    | FullTime = 1    
    | PartTime = 2    
    | Intern   = 3
    | Seasonal = 4
   
type Employee(emplNbr, fname, lname, status, salary) =
    member val EmployeeNumber = emplNbr with get, set
    member val FirstName = fname with get, set
    member val LastName = lname with get, set
    member val Status = status with get, set
    member val HourlySalary = salary with get, set    
    member this.Present() : unit =
        printfn "Employee Payroll Summary"
        printfn "----------------------------------------------------"
        printfn "Employee #:    %s" this.EmployeeNumber
        printfn "Full Name:     %s %s" this.FirstName this.LastName
        printfn "Status:        %A" this.Status
        printfn "Hourly Salary: %0.02F" this.HourlySalary
        printfn "----------------------------------------------------"

let empl = Employee("7092-3094", "Rose", "Crittenden", EmploymentStatus.FullTime, 24.05)
let resume = Action(empl.Present)
resume.Invoke()

This would produce:

Employee Payroll Summary
----------------------------------------------------
Employee #:    7092-3094
Full Name:     Rose Crittenden
Status:        FullTime
Hourly Salary: 24.05
----------------------------------------------------
Press any key to close this window . . .

To support the parameters passed to a procedure, there are various versions of the Action delegate. To specify the parameters of an Action delegate, in its <> operator, enter the parameter types separated by commas. Here is an example:

Action<int, string>()

In the parentheses, pass the name of the function that implements the delegate.

Functions Delegates

Introduction

The Action delegate is used for unit functions (also called procedures) or class methods; those are functions and methods that don't return a value. To support functions and methods that return a value, the .NET Framework provides a delegate named Func. As seen for the Action type, the Func delegate is delivered in various versions that take different parameters.

The most fundamental version of a function delegate has the following signature:

type Func = 
    delegate of unit -> 'TResult

Notice that instead of the Action word, Func is used. This version is for a function or method that takes no parameter but returns a value. Notice that the return value is a generic type. This means that you must specify the type of value that the function or method must return. To do this, enter the type after the Func name between < and >. Everthing else is as we saw for the Action delegate. Remember that the function returns a value. You can get that value and store it in a variable if you want. Here is an example:

open System

let introduce() : string =
    "Welcome to Four-Corner restaurant. We serve the best exotic meals in the region. " +
    "Our selection includes soups, sauce, meals, and appetizers. During the week, " +
    "from Monday to Friday, we serve the Lunch Special when the prices of most of " +
    "meals are reduced. Come and try our tastes. You will be delighted."

let description = Func<string>(introduce)

let result = description.Invoke();

printfn "Restaurant Introduction: %s" result

This would produce:

Restaurant Introduction: Welcome to Four-Corner restaurant. We serve the best ex
otic meals in the region. Our selection includes soups, sauce, meals, and appeti
zers. During the week, from Monday to Friday, we serve the Lunch Special when th
e prices of most of meals are reduced. Come and try our tastes. You will be deli
ghted.
Press any key to close this window . . .

If you don't need to use the return value many times, you don't have to store it in a variable. You can directly call the Invoke() method where the return value is needed. Here is an example:

open System

let introduce() : string =
    "Welcome to Four-Corner restaurant. We serve the best exotic meals in the region. " +
    "Our selection includes soups, sauce, meals, and appetizers. During the week, " +
    "from Monday to Friday, we serve the Lunch Special when the prices of most of " +
    "meals are reduced. Come and try our tastes. You will be delighted."

let description = Func<string>(introduce)

printfn "Restaurant Introduction: %s" (description.Invoke())

In the same way, you don't have to formally declare a variable for Func. You can directly use it where needed. Here is an example:

printfn "Restaurant Introduction: %s" ((Func<string>(introduce)).Invoke())

As its name indicates, the primary purpose of a function delegate is to return a value. The value can be a primitive type or a class type. Here is an example:

open System

type Employee() =
    let mutable emplNbr = ""
    let mutable fn = ""
    let mutable ln = ""
    let mutable sal  = 0.00
    member this.EmployeeNumber with get() = emplNbr and set(value) = emplNbr <- value
    member this.FirstName with get() = fn and set(value) = fn <- value
    member this.LastName with get() = ln and set(value) = ln <- value
    member this.HourlySalary with get() = sal and set(value) = sal <- value

let createEmployeeRecord() =
    let empl : Employee = Employee()
    empl.EmployeeNumber <- "2080-4813"
    empl.FirstName <- "James"
    empl.LastName <- "Alexanders"
    empl.HourlySalary <- 22.25
    empl

let presentation = Func<Employee>(createEmployeeRecord)

let empl = presentation.Invoke()

printfn "Employee Payroll Summary"
printfn "----------------------------------------------------"
printfn "Employee #:    %s" empl.EmployeeNumber
printfn "Full Name:     %s %s" empl.FirstName empl.LastName
printfn "Hourly Salary: %0.02F" empl.HourlySalary
printfn "----------------------------------------------------"

This would produce:

Employee Payroll Summary
----------------------------------------------------
Employee #:    2080-4813
Full Name:     James Alexanders
Hourly Salary: 22.25
----------------------------------------------------
Press any key to close this window . . .

Characteristics of Function Delegates

We already know how to specify the return value of a function. One of the characteristics of delegates is that they can take parameters. This also applies to Functions. To specify the parameter of a Func delegate, in its <> operator, after <, enter the parameter type, followed by a comma, and the return type. Here is an example:

Func<int, string>()

In the parentheses, pass the name of the function that implements the delegate. When calling the Invoke() function, pass an argument that is the type specified in Func. Here is an example:

open System

let introduce() : string =
    "Welcome to Four-Corner restaurant. We serve the best exotic meals in the region. " +
    "Our selection includes soups, sauce, meals, and appetizers. During the week, " +
    "from Monday to Friday, we serve the Lunch Special when the prices of most of " +
    "meals are reduced. Come and try our tastes. You will be delighted."

let describeMeal selection : string =
    match selection with
    | 1 -> "Sweet and Sour Chicken is cooked from a combination of " +
           "vegetable oil, chicken breast, brown sugar, wine vinegar, salt pepper, green " +
           "pepper, cornstarch, and water. water. We serve it both at noon (lunch) and " +
           "evening (dinner). At lunch, we serve it both as Lunch Special (5.95) and " +
           "regular lunch (8.95). In the evening and weekend (all times), we serve it for 9.95."
    | 2 -> "Mongolian Beef is cooked in a mixure of flank steak, vegetable " +
           "oil, eggs, scallion slices, garlic, rice noodles, cornstarh, sugar, ground " +
           "black pepper, oy sauce, and water. We serve it both at noon (lunch) and " +
           "evening (dinner). At lunch, Lunch Special costs 5.95 and regular lunch " +
           "costs 8.95. In the evening and weekend (all times), we serve it for 9.95."
    | 3 -> "Our Cashew Shrimp meal is made from shelled srimp, vegetable " +
           "oil, cashews, carrots, diced celery, water chestnut, and rice wine. In " +
           "addition, we use checken broth, sesame oil, cornstarch, soy sauce, and " +
           "oyster sauce. We serve Cashew Shrimp everyday for lunch and dinner. Lunch " +
           "special costs 7.25. Evening diner and weekend meals cost 10.55"
    | _->  "The value you provided is not a valid selection."

let description = Func<int, string>(describeMeal)
let result = description.Invoke(2)

printfn "Restaurant Introduction: %s" ((Func<string>(introduce)).Invoke())
printfn "Description of Meal Selected\n%s" result

This would produce:

Restaurant Introduction: Welcome to Four-Corner restaurant. We serve the best ex
otic meals in the region. Our selection includes soups, sauce, meals, and appeti
zers. During the week, from Monday to Friday, we serve the Lunch Special when th
e prices of most of meals are reduced. Come and try our tastes. You will be deli
ghted.
Description of Meal Selected
Mongolian Beef is cooked in a mixure of flank steak, vegetable oil, eggs, scalli
on slices, garlic, rice noodles, cornstarh, sugar, ground black pepper, oy sauce
, and water. We serve it both at noon (lunch) and evening (dinner). At lunch, Lu
nch Special costs 5.95 and regular lunch costs 8.95. In the evening and weekend
(all times), we serve it for 9.95.
Press any key to close this window . . .

In the same way, a function can take more than one parameter and you must indicate their types. As mentioned already, the Func delegate is available in various versions for different numbers of parameters. To specify the types of the parameters, after the < operator of Func, enter the types separated by commas, and end with the return type of the function. Here is an example:

open System

type TimePeriod =
    | LunchSpecial
    | RegularLunch
    | Dinner
    
let introduce() : string =
    "Welcome to Four-Corner restaurant. We serve the best exotic meals in the region. " +
    "Our selection includes soups, sauce, meals, and appetizers. During the week, " +
    "from Monday to Friday, we serve the Lunch Special when the prices of most of " +
    "meals are reduced. Come and try our tastes. You will be delighted."

let describeMeal selection : string =
    match selection with
    | "SSC" -> "Sweet and Sour Chicken is cooked from a combination of " +
                "vegetable oil, chicken breast, brown sugar, wine vinegar, salt pepper, green " +
                "pepper, cornstarch, and water. water. We serve it both at noon (lunch) and " +
                "evening (dinner). At lunch, we serve it both as Lunch Special (5.95) and " +
                "regular lunch (8.95). In the evening and weekend (all times), we serve it for 9.95."
    | "MNB" -> "Mongolian Beef is cooked in a mixure of flank steak, vegetable " +
                "oil, eggs, scallion slices, garlic, rice noodles, cornstarh, sugar, ground " +
                "black pepper, oy sauce, and water. We serve it both at noon (lunch) and " +
                "evening (dinner). At lunch, Lunch Special costs 5.95 and regular lunch " +
                "costs 8.95. In the evening and weekend (all times), we serve it for 9.95."
    | "CSH" -> "Our Cashew Shrimp meal is made from shelled srimp, vegetable " +
                "oil, cashews, carrots, diced celery, water chestnut, and rice wine. In " +
                "addition, we use checken broth, sesame oil, cornstarch, soy sauce, and " +
                "oyster sauce. We serve Cashew Shrimp everyday for lunch and dinner. Lunch " +
                "special costs 7.25. Evening diner and weekend meals cost 10.55"
    | _ -> "The value you provided is not a valid selection."

let selectMeal (period : TimePeriod) choice : float =
    match period with
    | LunchSpecial ->
        match choice with
        | 1 -> 5.95         // Lunch Special
        | 2 -> 6.45         // Regular Lunch
        | 3 -> 7.25         // Dinner and Weekend
        | _ -> 0.00         // Unknown Choice
    | RegularLunch ->
        match choice with
        | 1 -> 8.95         // Lunch Special
        | 2 -> 10.50        // Regular Lunch
        | 3 -> 9.65         // Dinner and Weekend
        | _ -> 0.00         // Unknown Choice
    | Dinner ->
        match choice with
        | 1 -> 9.95         // Lunch Special
        | 2 -> 12.50        // Regular Lunch
        | 3 -> 9.65         // Dinner and Weekend
        | _ -> 0.00         // Unknown Choice
    
let selection = Func<TimePeriod, int, float>(selectMeal)
let cost = selection.Invoke(LunchSpecial, 2)

printfn "Restaurant Introduction: %s" ((Func<string>(introduce)).Invoke())
printfn "Description of Meal Selected\n%s" ((Func<string, string>(describeMeal)).Invoke("MNB"))
printfn "Meal Price: %0.02F" cost

This would produce:

Restaurant Introduction: Welcome to Four-Corner restaurant. We serve the best ex
otic meals in the region. Our selection includes soups, sauce, meals, and appeti
zers. During the week, from Monday to Friday, we serve the Lunch Special when th
e prices of most of meals are reduced. Come and try our tastes. You will be deli
ghted.
Description of Meal Selected
Mongolian Beef is cooked in a mixure of flank steak, vegetable oil, eggs, scalli
on slices, garlic, rice noodles, cornstarh, sugar, ground black pepper, oy sauce
, and water. We serve it both at noon (lunch) and evening (dinner). At lunch, Lu
nch Special costs 5.95 and regular lunch costs 8.95. In the evening and weekend
(all times), we serve it for 9.95.
Meal Price: 6.45
Press any key to close this window . . .

Remember that the parameters are generic and can be any type. In fact, you can use a combination of classes and primitive types.

A function delegate uses the same characteristics of regular functions and methods. For example, a function delegate can be passed as parameter to a function or a method. Here is an example:

open System

let initialyze courseName credits =
    courseName + "(" + (string credits) + ")"

let createCatalog (init : Func<string, int, string>) name title credits =
    let course =  new Func<string, int, string>(initialyze)
    name + ": " + course.Invoke(title, credits)

let catalogue = Func<Func<string, int, string>, string, string, int, string>(createCatalog)
let init = Func<string, int, string>(initialyze)
let result = catalogue.Invoke(init, "CMIS 226", "Enterprise Database Design", 3)

printfn "Course Decription: %s" result

This would produce:

Course Decription: CMIS 226: Enterprise Database Design(3)
Press any key to close this window . . .

Remember that you can omit declaring variables that you don't need (making code difficult to read). Here is an example:

open System

let initialyze courseName credits =
    courseName + "(" + (string credits) + ")"

let createCatalog (init : Func<string, int, string>) name title credits =
    let course =  new Func<string, int, string>(initialyze)
    name + ": " + course.Invoke(title, credits)

let result = (Func<Func<string, int, string>, string, string, int, string>(createCatalog)).Invoke(Func<string, int, string>(initialyze), "CMIS 226", "Enterprise Database Design", 3)

printfn "Course Decription: %s" result

This would produce:

Course Decription: CMIS 226: Enterprise Database Design(3)
Press any key to close this window . . .

As another characteiristic, a method of a class can be passed to a function delegate. Here is an example:

open System

type EmploymentStatus =
    | Unknown  = 0
    | FullTime = 1
    | PartTime = 2
    | Intern   = 3
    | Seasonal = 4
     
type Employee(emplNbr, fname, lname, status, salary) =
    member val EmployeeNumber = emplNbr with get, set
    member val FirstName = fname with get, set
    member val LastName = lname with get, set
    member val Status = status with get, set
    member val HourlySalary = salary with get, set
    
    member this.CalculateSalary salaryIsFixed timeWorked : float =
        if salaryIsFixed = true then
            salary * 40.00
        else
            salary * timeWorked

let empl = Employee("7092-3094", "Rose", "Crittenden", EmploymentStatus.FullTime, 24.05)
let result = Func<bool, float, float>(empl.CalculateSalary)

printfn "Employee Payroll Summary"
printfn "----------------------------------------------------"
printfn "Employee #:    %s" empl.EmployeeNumber
printfn "Full Name:     %s %s" empl.FirstName empl.LastName
printfn "Status:        %A" empl.Status
printfn "Hourly Salary: %0.02F" empl.HourlySalary
printfn "----------------------------------------------------"
printfn "Weekly Salary: %0.02F" (result.Invoke(false, 44.50))

This would produce:

Employee Payroll Summary
----------------------------------------------------
Employee #:    7092-3094
Full Name:     Rose Crittenden
Status:        FullTime
Hourly Salary: 24.05
----------------------------------------------------
Weekly Salary: 1070.23
Press any key to close this window . . .

Previous Copyright © 2015-2024, FunctionX Thursday 02 November 2024 Next