Question

The F# tutorial includes the following snippet:

/// A record for a person's first and last name
type Person = {     
    First : string
    Last  : string
}

/// define a discriminated union of 3 different kinds of employees
type Employee = 
    | Engineer  of Person
    | Manager   of Person * list<Employee>            // manager has list of reports
    | Executive of Person * list<Employee> * Employee // executive also has an assistant

The fact that Manager and Executive are described as tuples offends my sensibilities (I'm easily offended). It strikes me as not terribly expressive. I tried to modify them as follows:

/// define a discriminated union of 3 different kinds of employees
type Employee = 
    | Engineer  of Person
    | Manager   of { Name: Person; Reports: Employee list }            // manager has list of reports
    | Executive of { Name: Person; Reports: Employee list; Assistant: Employee}   // executive also has an assistant

Sadly, the definitions of Manager and Executive now give an error: "This construct is deprecated; consider using a separate record type instead." Ok, seems fair, let's call it ManagerType. But wait ... ManagerType refers to Employee (for the Reports part), and Employee refers to ManagerType (for the Manager option).

Is there a solution here? Is it not possible for two data structures to be defined in terms of each other?

Was it helpful?

Solution

You can define mutually dependent types using and:

    type Employee = 
    | Engineer  of Person
    | Manager   of Manager            // manager has list of reports
    | Executive of Executive
    and Manager = { Name: Person; Reports: Employee list }
    and Executive = { Name: Person; Reports: Employee list; Assistant: Employee }

OTHER TIPS

If you're using F# v3.1 you can use named union fields [MSDN] (and protect your delicate sensibilities):

type Employee = 
    | Engineer  of Person
    | Manager   of Name: Person * Reports: Employee list
    | Executive of Name: Person * Reports: Employee list * Assistant: Employee

Mutually recursive types are declared with type ... = ... and ... = ...:

type Employee = 
    | Engineer  of Person
    | Manager   of Manager 
    | Executive of Executive
and Manager = { Name: Person; Reports: Employee list }
and Executive = { Name: Person; Reports: Employee list; Assistant: Employee}

As of FSharp 4.6 you can use Anonymous Records with unions. So with your example can be defined in this way:

type Person = {     
    First : string
    Last  : string
}

type Employee =
    | Engineer of Person
    | Manager of {| Name: Person; Reports: Employee list |}
    | Executive of {| Name: Person; Reports: Employee list; Assistant: Employee |}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top