Home

Delegates

 

Fundamentals of Delegates

 

Introduction

A delegate is a special type of user-defined variable that is used as a function. The delegate is created globally as a variable that can be accessed anywhere in the program. The delegate is not actually defined. It represents or show what an action function would look like. To support this concept, a delegate can provide all the necessary information that would be used on a function or method. This includes a return type, either no argument or one or more arguments.

Creating a Simple Delegate

The basic formula to create a delegate is:

type delegate-typename = delegate of ParameterType -> ReturnType

The type keyword and the = delegate of expression are required. The delegate-typename is the name of this delegate. The name follows the rule for functions or variables. The ParameterType and ReturnType can be any of the data types we have used so far. Any of them can also be a type unit or the name of a class. Here is an example:

type delSomething = delegate of unit -> unit

This declararion indicates a delegate that represents a simple function that takes no parameter and doesn't return any value. After creating a delegate, remember that it only provides a template for a function or method, not an actual function. In order to use it, you must define a function that would carry an assignment. That function must have the same return type and the same (number of) argument(s), if any. Here is an example of such a function:

let announce() =
    sprintf "Welcome to the wonderful world of F# programming."

Introduction to F# Support for Delegates

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

FSharpFunc represents the delegate you have created. It works as follows: every time you create a delegate, it becomes a class derived from FSharpFunc. Because FSharpFunc has a constructor and methods.

After creating the function, you must associate it to the delegate. To do that, where you want to use the function, declare a variable (you can optionally indicate that the variable is of the type of the delegate). Call the constructor of the delegate . In the parentheses of the delegate constructor, pass the name of the function.

Associating a Function to a Delegate

A delegate represents the signature of a function. In order to use it, you must associate it to a function. If you don't have one already, you can start by creating a function. As mentioned already, 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 VariableName = new DelegateName(FunctionName)

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.

Invoking a Delegate

To let you actually use the variable assigned from the delegate, the FSharpFunc class, which is the parent of the delegate variable, is equipped with a method named Invoke. Call that method on the name of the variable. To support delegates, the .NET Framework provides a class named Delegate.

A Delegate That Returns a Value

Remember that a delegate presents the prototype of a function. Based on this, if you want to associate a function that returns a value, your delegate must indicate it. Here is an example:

type SimpleMessage = delegate of unit -> string

This declaration is for a delegate that takes no parameter but returns a stringt to the name of the delegate. You must associate it to a function of the same signature. In this case, you can get the returned value by calling the Invoke() method and use the value as you see fit. Here is an example

open System
open System.Windows.Forms 

type SimpleMessage = delegate of unit -> string

let announce() : string = "Welcome to the wonderful world of F# programming."

let msg = new SimpleMessage(announce)

let exercise = new Form(Width = 300, Height = 80, Text = "Exercise")

let lblMessage = new Label(Left = 21, Top = 18, Width = 280)
exercise.Controls.Add lblMessage

lblMessage.Text <- msg.Invoke()

do Application.Run exercise

This would produce:

Delegate

Delegates and Parameters

 

Passing an Argument to a Delegate

Since a delegate is primarily the prototype of a function, it can take an argument. The formula to pass a parameter to a delegate is:

type delegate-typename = delegate of Parameter -> ReturnType

Everything is primarily done as we have seen so far. Here is an example:

open System
open System.Windows.Forms 

type Description = delegate of string -> unit

let exercise = new Form(Width = 220, Height = 80, Text = "Exercise")

let lblMessage = new Label(Left = 21, Top = 18, Width = 280)
exercise.Controls.Add lblMessage

let showTitle title = lblMessage.Text <- "Title: " + title

let ttl = new Description(showTitle)

do Application.Run exercise

To let you call the function on the variable, we saw that the FSharpFunc class is equipped with the Invoke() method. Here is an example:

open System
open System.Windows.Forms 

type Description = delegate of string -> unit

let exercise = new Form(Width = 220, Height = 80, Text = "Exercise")

let lblMessage = new Label(Left = 21, Top = 18, Width = 280)
exercise.Controls.Add lblMessage

let showTitle title = lblMessage.Text <- "Title: " + title

let ttl = new Description(showTitle)

ttl.Invoke "Parallel Programming in F#"

This would produce:

Passing an Argument to a Delegate

In the same, you can have a delegate that takes a parameter and returns a value. Here is an example:

open System
open System.Windows.Forms

type Solution = delegate of float -> string

let exercise = new Form(Width = 155, Height = 80, Text = "Exercise")

// Label: Solution
exercise.Controls.Add(new Label(Left = 21, Top = 18, Width = 40, Text = "Result:"))

// Text Box: Solution 
let txtSolution = new TextBox(Left = 69, Top = 15, Width = 50)
exercise.Controls.Add txtSolution

let solve(value : float) : string = (string (value * 3.00))

let result = new Solution(solve)

txtSolution.Text <- result.Invoke 224.68

do Application.Run exercise

This would produce:

Passing an Argument to a Delegate

Passing Many Arguments to a Delegate

If you want to use a function that takes more than one argument, the formula to create its delegate is:

type delegate-typename = delegate of ParameterType1 * ParameterType2 * ... * ParameterType_n -> ReturnType

After the delegate of expression, specify the data type of each parameter. Separate them with *. Here is an example of a delegate that takes two floating-point numbers and returns a string:

type Calculation = delegate of float * float -> string

Remember that the function must use the same number of arguments and the same return type as the delegate. When calling the Invoke() method, pass the same number of arguments as the function and use the return value as you see fit. Here is an example:

open System
open System.Windows.Forms

type Calculation = delegate of float * float -> float

let calculate (a : float) (b : float) : float = a * b

let msg : Calculation = new Calculation(calculate)

let exercise = new Form(Width = 300, Height = 80, Text = "Exercise")

let lblMessage = new Label(Left = 21, Top = 18, Width = 280)
exercise.Controls.Add lblMessage

lblMessage.Text <- "48.88 * 137.27 = " + string (msg.Invoke(48.88, 137.27))

do Application.Run exercise

This would produce:

Passing an Argument to a Delegate

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 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:

open System
open System.Windows.Forms

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)

let exercise = new Form(Width = 350, Height = 80, Text = "Exercise")

let lblCourse = new Label(Left = 21, Top = 18, Width = 320)
exercise.Controls.Add lblCourse

lblCourse.Text <- "Course Decription: " + result

do Application.Run exercise

This would produce:

A Delegate as a Parameter

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

open System
open System.Windows.Forms

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 + ": " + (Initialyzer(initialyze)).Invoke(title, credits)

let catalogue = new Cataloguer(createCatalog)

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

let exercise = new Form(Width = 350, Height = 80, Text = "Exercise")

let lblCourse = new Label(Left = 21, Top = 18, Width = 320)
exercise.Controls.Add lblCourse

lblCourse.Text <- "Course Decription: " + result

do Application.Run exercise

Or this:

open System
open System.Windows.Forms

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 + ": " + (Initialyzer(initialyze)).Invoke(title, credits)

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

let exercise = new Form(Width = 350, Height = 80, Text = "Exercise")

let lblCourse = new Label(Left = 21, Top = 18, Width = 320)
exercise.Controls.Add lblCourse

lblCourse.Text <- "Course Decription: " + result

do Application.Run exercise

Or even this:

open System
open System.Windows.Forms

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 + ": " + (Initialyzer(initialyze)).Invoke(title, credits)

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

let exercise = new Form(Width = 350, Height = 80, Text = "Exercise")

let lblCourse = new Label(Left = 21, Top = 18, Width = 320)
exercise.Controls.Add lblCourse

lblCourse.Text <- "Course Decription: " + ((new Cataloguer(createCatalog)).Invoke(new Initialyzer(initialyze), "CMIS 226", "Enterprise Database Design", 3))

do Application.Run exercise

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. Here is an example that uses a method that associates to a delegate that takes no parameter and returns nothing:

open System
open System.Windows.Forms

type Creation = delegate of unit -> unit

let exercise = new Form(Width = 200, Height = 160, Text = "Exercise")

let lblEmployeeNumber = new Label(Left = 23, Top = 18, Width = 200)
exercise.Controls.Add(lblEmployeeNumber)

let lblEmployeeName = new Label(Left = 23, Top = 43, Width = 200)
exercise.Controls.Add lblEmployeeName

let lblEmploymentStatus = new Label(Left = 23, Top = 71, Width = 200)
exercise.Controls.Add lblEmploymentStatus

let lblHourlySalary = new Label(Left = 23, Top = 96, Width = 200)
exercise.Controls.Add lblHourlySalary

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 =
        lblEmployeeNumber.Text <- "Employee #: " + this.EmployeeNumber
        lblEmployeeName.Text <- "Full Name: " + this.FirstName + " " + this.LastName
        lblHourlySalary.Text <- "Status: " + this.Status
        lblEmploymentStatus.Text <- "Hourly Salary: " + (string this.HourlySalary)

let empl = Employee("7092-3094", "Rose", "Crittenden", "Full-Time", 24.05)

let resume = Creation(empl.Present)

resume.Invoke()

do Application.Run exercise

This would produce:

A Delegate as a Parameter

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:

open System
open System.Windows.Forms

type Calculation = delegate of float -> float

let exercise = new Form(Width = 200, Height = 200, Text = "Exercise")

let lblEmployeeNumber = new Label(Left = 23, Top = 18, Width = 200)
exercise.Controls.Add lblEmployeeNumber

let lblEmployeeName = new Label(Left = 23, Top = 43, Width = 200)
exercise.Controls.Add lblEmployeeName

let lblEmploymentStatus = new Label(Left = 23, Top = 71, Width = 200)
exercise.Controls.Add lblEmploymentStatus

let lblHourlySalary = new Label(Left = 23, Top = 96, Width = 200)
exercise.Controls.Add lblHourlySalary

let lblLine = new Label(Width = 150, Left = 23, Top = 122)
lblLine.Text <- "-------------------------------------"
exercise.Controls.Add lblLine

let lblWeeklySalary = new Label(Left = 23, Top = 145, Width = 200)
exercise.Controls.Add lblWeeklySalary

type Employee() =
    static let mutable emplNbr = ""
    static let mutable fn = ""
    static let mutable ln = ""
    static let mutable status = "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 timeWorked = sal * timeWorked

Employee.EmployeeNumber <- "8485-2082"
Employee.FirstName <- "Joseph"
Employee.LastName <- "Almunia"
Employee.Status <- "Full-Time"
Employee.HourlySalary <- 32.25

let result = Calculation(Employee.CalculateSalary)

lblEmployeeNumber.Text <- "Employee #: " + Employee.EmployeeNumber
lblEmployeeName.Text <- "Full Name: " + Employee.FirstName + " " + Employee.LastName
lblHourlySalary.Text <- "Status: " + Employee.Status
lblEmploymentStatus.Text <- "Hourly Salary: " + (string Employee.HourlySalary)
lblWeeklySalary.Text <- "Weekly Salary: " + (string (result.Invoke(44.50)))

do Application.Run exercise

This would produce:

A Delegate as a Parameter

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 SomeClass -> unit

This an example of a delegate for a function that would take a parameter based a class named SomeClass. 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 already.

As mentioned for functions, you can pass a combination of parameters of various types such as 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:

open System
open System.Windows.Forms

let exercise = new Form(Width = 200, Height = 130, Text = "Exercise")

let lblEmployeeNumber = new Label(Left = 23, Top = 18, Width = 200)
exercise.Controls.Add lblEmployeeNumber

let lblEmployeeName = new Label(Left = 23, Top = 43, Width = 200)
exercise.Controls.Add lblEmployeeName

let lblHourlySalary = new Label(Left = 23, Top = 71, Width = 200)
exercise.Controls.Add lblHourlySalary

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()

lblEmployeeNumber.Text <- "Employee #: " + empl.EmployeeNumber
lblEmployeeName.Text <- "Full Name: " + empl.FirstName + " " + empl.LastName
lblHourlySalary.Text <- "Hourly Salary: " + (string empl.HourlySalary)

do Application.Run exercise

This would produce:

A Delegate that Returns an Object

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. When calling the primary function, pass the second function as argument in the placeholder of the function passed as parameter. 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. When calling the primary function, remember to pass a secondary function as parameter. Remember that you must pass an argument to that parameter.

Instead of returning unit, 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:

open System
open System.Windows.Forms 

let exercise = new Form(Width = 390, Height = 80, Text = "Exercise")

let lblMessage = new Label(Left = 21, Top = 18, Width = 360)
exercise.Controls.Add lblMessage

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

let result = display present "Welcome to the wonderful world of functional programming!"
lblMessage.Text <- "Message: " + result

do Application.Run exercise

This would produce:

A Delegate that Returns an Object

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:

open System
open System.Windows.Forms 

let exercise = new Form(Width = 240, Height = 80, Text = "Exercise")

let lblMessage = new Label(Left = 21, Top = 18, Width = 220)
exercise.Controls.Add lblMessage

let createCatalog (description : string -> string) name teacher period =
    "Teacher: " + teacher + "\n\n" + description name + "\n\nSchedule: " +  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"

lblMessage.Text <- catalog

do Application.Run exercise

This would produce:

A Delegate that Returns an Object

You can pass as many function parameters as you want to a primary function. For any function parameter that is not a unit, 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.

   
 

Previous Copyright © 2015 FunctionX Next