Fundamentals of Active Patterns

Introduction

An active pattern is a special name that either you can use as a function or you can involve in pattern matching.

The primary formula to create an active pattern for a function is:

let (|function-name|) [ argument(s) ] = body

You start with the let keyword followed by parentheses. In the parentheses, add two pipes. Inside the pipes, add a name for a function. By tradition, the name inside the pipes uses the convention for types. Here is an example:

let (|Show|) . . .

Outside the parentheses, if the function will not take an argument, type some empty parentheses or an underscore. This is followed by the normal = sign of a regular function. As you know already, the section after = is the body of the function. If you are not planning to do anything significant, you can use () as the whole body of the function. Here is an example:

let (|Show|) _ = ()

Using a Functional Active Pattern

The function as we have just created it is primarily a regular function like any other. This means that you can use it when necessary. To call the function, include it in its pipes and parentheses. If the function didn’t take any argument, make sure you add empty parentheses to it. Here is an example:

let (|Show|) _ = ()

(|Show|)()

This would produce:

Press any key to close this window . . .

You can make the function behave like any other. For example, in its body, you can display something. Here is an example:

let (|Show|) _ = printfn "The wonderful world of functional programming"

(|Show|)()

This would produce:

The wonderful world of functional programming
Press any key to close this window . . .

A Functional Active Pattern with Argument

You can pass an argument to the function. This is done just after the parentheses of the function name. Here is an example:

let (|Calculate|) x = ...

Of course, you can specify the data type of the argument. In this case, you must include it in parentheses, with its type, both separated by a colon. This would be done as follows:

let (|Calculate|) (x : float) = ...

In the body of the function, use the argument as you see fit. Here is an example for a function used to calculate the area of a circle while its argument represents the radius of the circle:

let (|Calculate|) x = x * x * 3.14

You can then call the function and pass the desired value to it. Here is an example:

let (|Calculate|) x = x * x * 3.14

let area = (|Calculate|) 25.50

printfn "Area of Circle: %0.04f" area

This would produce:

Area of Circle: 2041.7850
Press any key to close this window . . .

Otherwise, you can declare a variable for the argument that you will pass to the function. Here is an example:

let (|Calculate|) x = x * x * 3.14

let radius = 48.79

let area = (|Calculate|) radius

printfn "Circle Characteristics"
printfn "----------------------"
printfn "Radius: %0.02f" radius
printfn "Area:   %0.04f" area

This would produce:

Circle Characteristics
----------------------
Radius: 48.79
Area:   7474.6573
Press any key to close this window . . .

Passing Arguments to a Functional Active Pattern

You can pass as many arguments as you want, and you can use the arguments any way you want. Here is an example:

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

This would produce:

Rectangle Characteristics
----------------------
Length: 48.79
Height: 33.63
Area:   1640.8077
Press any key to close this window . . .

Of course, you can do more than a short simple calculation in the function.

Matching an Active Pattern

An active pattern can be used as the expression of a matching pattern. Here is an example:

let (|Show|) _ = ()

match (|Show|) with

If the active pattern was created as a function, the matching pattern should have only one option. If the function of the active pattern didn’t take an argument, you can use anything as the option of the matching pattern. Here is an example:

let (|Show|) _ = ()

match (|Show|) with
| anything ->

Since that option doesn’t hold any value, it cannot be used; it is just an empty insignificant value. Therefore, as an alternative, you can use the underscore. Here is an example:

let (|Show|) _ = ()

match (|Show|) with
| _ ->

In the body of the option, you can call the function if you want. In this case, you must use the whole name, including its parentheses and pipes. If the function didn’t take an argument, add some empty parentheses to it. Here is an example:

let (|Show|) _ = ()

match (|Show|) with
| _ -> (|Show|)()

As seen earlier, you can put any statement of your choice in the body of the function, such as displaying something. Here is an example:

let (|Show|) _ = printfn "The wonderful world of functional programming"

match (|Show|) with
| _ -> (|Show|)()

This would produce:

The wonderful world of functional programming
Press any key to close this window . . .

You can also use the option in the pattern matching any way you want, such as displaying a value. Here is an example:

let (|Show|) _ = ()

match (|Show|) with
| _ -> printfn "This is the wonderful world of F# programming"

This would produce:

This is the wonderful world of F# programming
Press any key to close this window . . .

In most cases, you create a functional active pattern because you want to perform an operation. In this case, you should pass an argument to the function. In the body of the function, use the argument as you see fit. Here is an example we saw earlier:

let (|Calculate|) x = x * x * 3.14

As we saw earlier, you can then call the function and pass the desired value to it. You can also call the function as the pattern to match and pass the necessary value for its argument. Here is an example:

let (|Calculate|) x = x * x * 3.14

match (|Calculate|) 25.50 with
| _ -> printfn "Area of Circle: %0.04f" ((|Calculate|) 25.50) 

Or you can declare a variable for the argument, initialize that variable, and pass it to the function. Here is an example:

let (|Calculate|) x = x * x * 3.14

let radius = 48.79

let area = (|Calculate|) radius

match area with
| _ -> printfn "Area of Circle: %0.04f" area

Of course, you can create more than one statement in the pattern matching. Here is an example:

let (|Calculate|) x = x * x * 3.14

let radius = 48.79

let area = (|Calculate|) radius

match area with
| _ -> printfn "Circle Characteristics"
       printfn "----------------------"
       printfn "Radius: %0.02f" radius
       printfn "Area:   %0.04f" area

This would produce:

Circle Characteristics
----------------------
Radius: 48.79
Area:   7474.6573
Press any key to close this window . . .

Remember that you can pass as many arguments as you want to the function. Here is an example we saw earlier:

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

You can also call the function as the option of the pattern matching. If the function was passed an argument, you can pass it any name as argument. Here is an example:

let (|Calculate|) x = x * x * 3.14

let radius = 48.88

match radius with
| Calculate something -> printfn "Area:   %0.04f" something

Partitioning an Active Pattern

Introduction

As you can seen and we will see, there are various ways active patterns are used. We saw that you can create a name for a function, define it, and use it like any normal function. Another option is to create various names (one to seven) that you can use in a matching pattern. The formula to follow is:

let (|name1|name2|name_n|) [ argument(s) ] = body

In the parentheses, create one to seven names. Each name must be included inside pipes. Here is an example:

let (|Square|Circle|) . . .

This time too, the argument(s) is (are) not (a) requirement but you should add at least one. The argument is used to let you choose what name will be used and when. In this case, the argument should be an integer. In the body of the active pattern, you can create a conditional statement or a matching pattern to associate (select) each option based on a certain value. Here is an example:

let (|Square|Circle|) x =
    match x with
    | 1 -> Square
    | _ -> Circle
    // if x = 1 then Square
    // else Circle

You can then use the active pattern in another statement. For example, you can define a function that will take action based on what name is valid. Here is an example:

let (|Square|Circle|) x =
    match x with
    | 1 -> Square
    | _ -> Circle

let calculateArea shape value =
    match shape with
    | Square -> value * value
    | Circle -> value * value * 3.14

let area = calculateArea 1 26.79

printfn "Area: %0.04f" area

This would produce:

Area: 717.7041
Press any key to close this window . . .

Partial Active Patterns

Another way you can use an active pattern is to validate a choice while not caring for another. This is referred to as a partial active pattern. The formula to create it is:

let (|name|_|) [ argument(s) ] = body

Add three pipes in the parentheses. In the left section, add a name. In the right section, use an underscore. This time also, you should add an argument. The argument will be used to validate the lone active pattern. Here is an example:

let (|Sphere|_|) x =
    match x with
    | x -> Some(x)

let calculateArea volume value =
    match volume with
    | Sphere -> value * value * 4.00 * 3.14
    | _ -> 0.00

let area = calculateArea () 26.79

printfn "Area: %0.04f" area

This would produce:

Area: 9014.3635
Press any key to close this window . . .

Previous Copyright © 2024, FunctionX Monday 14 February 2016 Next