Alexander Zeitler
Alexander Zeitler

Reputation: 13109

Recursive Union Type and List

I'm starting with F# and I read about recursive Union Types on https://fsharpforfunandprofit.com/posts/fsharp-in-60-seconds//

Given the example from the link

type Employee =
  | Worker of Person
  | Manager of Employee list

How can I create a value of type Manager and assign a List of Workers to it?

I can create a value of type Worker like this:

let john = {First = "John"; Last="Doe"}
let worker = Worker john

But I don't know how to create a value jane of type Manager having john assigned as an item of the Empoyee list:

let jane =  { First="Jane"; Last="Doe" }
let manager = Manager [worker]

This works, but I can't assign First and Last from jane to manager.

Is manager meant to have no First / Last at all in this example?

How would I have to change the type definition for Manager to have both a list of Employee?

In addition it also looks like Manager doesn't have a member employees I can access after creating a value of type Manager?

Upvotes: 3

Views: 100

Answers (1)

Fyodor Soikin
Fyodor Soikin

Reputation: 80805

Given that type definition, a manager cannot have first/last name. The type definition does not include anything like that.

To make Manager work similarly to Worker in this regard, just give it a Person value:

type Employee = Worker of Person | Manager of Person * Employee list

Then you can create a manager Jane:

let manager = Manager ({ First = "Jane"; Last = "Doe" }, [worker])

As for a member employees, there are several things to say.

First, stay away from members. F# is functional-first. Functions are better than members (for reasons that I'm not going to go into here). Have a function:

let employees emp = 
    match emp with
    | Manager (_, emps) -> emps
    | Worker _ -> ???  // what to return if the employee turned out to be worker?

And right away you can see a difficulty: when the employee is a Manager, sure, we can return their employees. But what if it's a Worker? What to return then?

One reasonable suggestion would be to return an empty list, because, after all, a Worker doesn't have any employees working for them, right?

let employees emp = 
    match emp with
    | Manager (_, emps) -> emps
    | Worker _ -> []

Another possibility would be to return an Option. Which approach is "correct" depends on what you're going to do with the result, so only you can answer that.

But if you absolutely insist on having a member (and please, please carefully think about why you need it), you can definitely add a member to your type like this:

type Employee = Worker of Person | Manager of Person * Employee list
    with
        member this.employees =
          match this with
          | Manager (_, emps) -> emps
          | Worker _ -> []

Upvotes: 3

Related Questions