Home

Discrimnated Unions

 

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 formula to create a discriminated union is:

type UnionName =
    | CaseName1 [of [ field1 : ] type1 [ * [ field2 : ] type2 . . .type_x ]
    | CaseName2 [of [ field3 : ] type3 [ * [ field4 : ] type4 . . .type_y ]
    | CaseName_n [of [ field_n : ] type_n [ * [ field_n : ] type_n . . .type_z ]

You start with the required type keyword. The UnionName is a normal name, usually starting with an uppercase letter. Each line in the union's body starts with | followed by a member name, usually starting with an uppercase letter. Here are examples:

type School =
    | Student
    | Course
    | Building

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 each member to have a particular meaning behavior. As each 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
    | 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;

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;

This would produce:

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

Althoug we used only one value for each, a member of a union can use many internal value. 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 continue . . .
 
   
 

Built-In Discriminated Unions

 

Some 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;

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 continue . . .

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 continue . . .

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 continue . . .
 
 
   
 

Previous Copyright © 2014-2015 FunctionX Next