4 ms·
You have a point, and I think the source of the author's confusion is the article never actually demonstrates the need for union types. Simply put, his example
by steego 11y ago
You have a point, and I think the source of the author's confusion is the article never actually demonstrates the need for union types. Simply put, his example could have been simplified to this based on his use cases:
type ActiveUser = { Name: string; Subscriptions: Subscription list }
type DeletedUser = { Name: string; DeletedDate: DateTime }
let delete (activeUser:ActiveUser) =
{ Name = activeUser.Name; DeletedDate = DateTime.Now }
let undelete (deletedUser:DeletedUser) =
{ Name = deletedUser.Name; Subscriptions = [] }
let addSubscription subscription activeUser =
{ activeUser with Subscriptions = subscription::activeUser.Subscriptions}
Your approach with subtyping is a valid approach, but you should note that your example doesn't actually create immutable objects due to the way you add subscriptions. I converted your example to F#, so you can appreciate how subtyping in F# has a very natural flow that makes writing types very similar to writing functions.
type User internal(name:string, isActive:bool) =
member this.Name = name
member this.IsActive = isActive
type ActiveUser private(name:string, subscriptions:Subscription list) =
inherit User(name, true)
member this.Subscriptions = subscriptions :> IEnumerable<_>
member this.AddSubscription(s) = ActiveUser(name, s::subscriptions)
new(name) = ActiveUser(name, [])
type DeletedUser(name, deletedDate:DateTime) =
inherit User(name, false)
member this.DeletedDate = deletedDate
member this.Reactivate() = ActiveUser(name)