Fundamentals of Discrimnated Unions

Introduction

A union is an object made of different parts but only one of those parts must be validated or considered in a particular program. This means that, unlike a class where all of the members of the object are available any time, when a union is used, only one of its members must be selected and used. For this reason, it is called a discriminated union.

Creating a Union

The primary formula to create a discriminated union is:

type union-name = ...

You start with the required type keyword. The name of the union, union-name, follows the rules of names of classes. After the name of the union, type =

The body of a Union

The body of a union is the section after its = symbol.

Adding a Member to a Union

A union contains one or more members. The simplest union has only one member. To add such a member, in the body of the union, that is, after the equal symbol, type | followed by the name of the member. The names of members follow the rules of names of classes. Here is an example:

type School = | Student

As you can see, a discriminted union starts the same as an enumeration (in fact, you can define a union like the above example and stop there, which would make it similar to an enumeration). The difference is that you usually create a union because you want its member to have a particular meaningful behavior. As a result, a member of a union should have meaning; such a member can be a complete object. This means that a member of a discrimnated union can consist of one internal value or many values. If a member has one value, specify the data type of that member, follow its name by the of keyword followed by a known type. Here are examples:

type School = | Student of string

To define what the discrimnated union member does, create a pattern matching expression in which the union member has a case. In the case section of a member, create a name like a variable and use it. At a minimum, you can simply return that name using the -> operator. This can be done as follows:

type School = | Student of string

let describe studies = match studies with | Student fullName -> fullName

Using a Discriminated Union

To use a discriminated union, you can declare a variable and assign the matching case as if it were a constructor to which you pass a value. To show the value of a case, use %A in its placeholder of the printf of the printfn function. Here is an example:

type School = | Student of string

let describe studies = match studies with | Student fullName -> fullName

let std = Student("Yamaguchi")
printfn "%A" std

This would produce:

Student "Yamaguchi"

Press any key to close this window . . .

A Union with many Members

The Members of a Union

Although we introduced unions with only one member, a typical union has more than one member. Such members a primarilly created as we saw for an enumeration. Here are examples:

type School =
    | Student
    | Course
    | Building

One of the primary differences between an enumeration and a discriminted union is that you usually create a union because you want each member to have a particular meaning. As each member of a union should have meaning, such a member can be a complete object. As a result, the updated formula to create a discriminated union is:

type union-name =
    | case-name-1 [of [ field1 : ] type1 [ * [ field2 : ] type2 . . .type_x ]
    | case-name-2 [of [ field3 : ] type3 [ * [ field4 : ] type4 . . .type_y ]
    | case-name_n [of [ field_n : ] type_n [ * [ field_n : ] type_n . . .type_z ]

Based on this, start the creation of a member as a case of a matching pattern; that is, a | symbol followed by a name. A member of a discrimnated union can consist of one internal value or many values. If a member has one value, specify the data type of that member. To do this, follow the name of the member with the of keyword followed by a known type. Here are examples:

type School =
    | Student of string
    | Course of string
    | Building of string

To define what the discrimnated union members do, create a pattern matching expression in which each union member has a case. In the case section of a member, create a name like a variable and use it. At a minimum, you can simply return that name using the -> operator. This can be done as follows:

let describe studies =
    match studies with
    | Student fullName -> fullName
    | Course code -> code
    | RoomNumber room -> room

Using a Discriminated Union

To use a discriminated union, you can declare a variable and assign the matching case as if it were a constructor to which you pass a value. To show the value of a case, use %A in its placeholder of the printf of the printfn function. Here are examples:

type School =
    | Student of string
    | Course  of string
    | Room of string

let describe studies =
    match studies with
    | Student fullName -> fullName
    | Course code -> code
    | Room nbr -> nbr

let std = Student("Yamaguchi")
printfn "%A" std

let crs = Course("Introduction to Web Design")
printfn "%A" crs

let rm = Room("Science and Aircraft")
printfn "%A" rm;

This would produce:

Student "Yamaguchi"
Course "Introduction to Web Design"
Room "Science and Aircraft"
Press any key to close this window . . .

You can also first declare a variable for each value and pass that variable to the case. Here are examples:

type School =
    | Student of string
    | Course  of string
    | Room of string

let describe studies =
    match studies with
    | Student fullName -> fullName
    | Course code -> code
    | Room nbr -> nbr

let name = "Yamaguchi"
let std = Student(name)
printfn "%A" std

let study = "Introduction to Web Design"
let crs = Course(study)
printfn "%A" crs

let building = "Science and Aircraft"
let rm = Room(building)
printfn "%A" rm

Although we used only one value for each, a member of a union can use many internal values. To indicate that a member is made of more than one value, add * and the data type of that member. Here are examples:

type School =
    | Student of string * char * string
    | Course  of string * int
    | Room of string * string

To specify the composition of a member, pass the appropriate arguments to its name, the way you would do as if it was a constructor. Outside of the parentheses, type -> followed by the expression that constitutes the value of the member. Here are examples:

let describe studies =
    match studies with
    | Student (f, m, l) -> f + " " + (string m) + ". " + l
    | Course (code, credit) -> code + (string credit)
    | Room (building, nbr) -> building + " " + nbr

To use a member, pass the appropriate values to its constructor and assign it to the desired variable. Here are examples:

type School =
    | Student of string * char * string
    | Course  of string * int
    | Room of string * string

let describe studies =
    match studies with
    | Student (f, m, l) -> f + " " + (string m) + ". " + l
    | Course (code, credit) -> code + (string credit)
    | Room (building, nbr) -> building + " " + nbr

let firstName = "Paul"
let lastName = "Yamaguchi"
let middleInitial = 'B'
let std = Student(firstName, middleInitial, lastName)
printfn "%A" std

let study = "Introduction to Web Design"
let crd = 3;
let crs = Course(study, crd)
printfn "%A" crs

let building = "Science and Aircraft"
let rmNbr = "A104"
let rm = Room(building, rmNbr)
printfn "%A" rm

This would produce:

Student ("Paul",'B',"Yamaguchi")
Course ("Introduction to Web Design",3)
Room ("Science and Aircraft","A104")
Press any key to close this window . . .

Built-In Discriminated Unions

Optional Arguments in a Function

A parameter is referred to as optional if a function can use it or not. For example, when you call a function, you can specify the value(s) of the parameter(s) or omit it (them). This means that, by default, all function parameters in F# are optional. Still, when you are creating a function, you can indicate that the caller will have the ability to pass or omit an argument.

To specify that a parameter is optional, first add the parentheses to the function. In the parentheses, type the name of the parameter, followed by a colon and its data type, followed by option. Here is an example:

let getMembership (c : int option) =
    . . .

You can then use the argument in the function or not. Here is an example:

let getMembership (c : int option) =
    printfn "Public Library Acknowledgement: %A" c

A Generic Discriminated Union

When you call a function, the F# language allows you to find out whether an argument was passed or not. To assist you with this, the F# language provides a generic discriminated union named Option. It is defined as follows:

type Option<'a> =
    | Some of 'a
    | None

The Option union, which is also used as option, has two members: Some and None. The formula to use it is:

match Value with
    | Some Value -> Some Statement
    | None -> None Statement

The Some member is used to validate or acknowledge that an argument, represented here by Value, was passed to the function. If no argument was passed, the None member is valid. After each option, type -> followed by what you want to do. Here is an example:

let getMembership (c : int option) =
    match c with
    | Some c -> "Valid Membership"
    | None -> "Unknown Category";

When calling the function, you can omit the argument if you want, in which case the function would not treat it. Here is an example:

let getMembership (c : int option) =
    match c with
    | Some c -> "Valid Membership"
    | None -> "Unknown Category"

let result = getMembership
printfn "Library Membership Category: %A" result;

If you want to pass the argument, preceded its value by the Some word. Here is an example:

let getMembership (c : int option) =
    match c with
    | Some c -> "Valid Membership"
    | None -> "Unknown Category"

let result = getMembership (Some 2)
printfn "Library Membership Category: %A" result;

This would produce:

Library Membership Category: "Valid Membership"
Press any key to close this window . . .

In the same way, in the Some section, now that you know an argument was passed, you can process it as you see fit. Here is an example:

let getMembership (c : int option) =
    match c with
    | Some c ->
        if c = 1 then "Teen"
        elif c = 2 then "Adult"
        elif c = 3 then "Senior"
        else "Invalid Category"
    | None -> "Empty";

let result = getMembership (Some 2)
printfn "Library Membership Category: %s" result;

This would produce:

Library Membership Category: Adult
Press any key to close this window . . .

Of course, you can use pattern matching in the Some section if you want. Here is an example:

let getMembership (c : int option) =
    match c with
    | Some c ->
        match c with
        | 1 -> "Teen"
        | 2 -> "Adult"
        | 3 -> "Senior"
        | _ -> "Invalid Category"
    | None -> "Empty";

let result = getMembership (Some 1)
printfn "Library Membership Category: %s" result;

This would produce:

Library Membership Category: Teen
Press any key to close this window . . .

Previous Copyright © 2014-2024, FunctionX Tuesday 31 October 2023 Next