F# matching records and discriminated unions - f#

I want to use calculateWage function, it gives an error that expected to have Employee type but here has type Person.
type Person =
{ first_name: string
last_name: string
age: int
salary_hour: int }
type Employee =
| Administrator of Person
| OfficeWorker of Person
| WarehouseWorker of Person
let calculateWage (employee:Employee) (hours:int) =
match employee with
| {salary_hour= sh} -> (sh * hours)*TAX/100

You need to match on the discriminated union like so:
let calculateWage (employee:Employee) (hours:int) =
match employee with
| Administrator {salary_hour= sh}
| OfficeWorker {salary_hour= sh}
| WarehouseWorker {salary_hour= sh} -> (sh * hours)*TAX/100
In this case it may seem stupid, but remember that each discriminated union case can have different data.
Often when I end up with data like this I do this in two steps. I have a function that extracts the common data. And a function that works with the data itself:
let extractPerson employee =
match employee with
| Administrator p
| OfficeWorker p
| WarehouseWorker p -> p
let calculateWage person (hours:int) =
(person.salary_hour * hours)*TAX/100
So you end up with some functions that you can easily compose:
let calculate employee =
employee
|> extractPerson
|> calculateWage

I'd go with Tom Moers's answer, but if you are insisting on having the deconstruction of Employee inside the pattern match, you can easily convert it into an active recognizer enclosed by "banana clips":
let (|Employee|) = function
| Administrator p
| OfficeWorker p
| WarehouseWorker p -> p
let calculateWage (employee:Employee) (hours:int) =
match employee with
| Employee{salary_hour= sh} -> (sh * hours)*TAX/100

You need to match on employee to to get the information:
let calculateWage (employee:Employee) (hours:int) =
match employee with
| Administrator person ->
(person.salary_hour * hours)*TAX/100})
| OfficeWorker person -> ...
| WarehouseWorker person -> ...
Alternatively, change calculate wage to accept a Person as argument.
This would be especially useful if all use the same calculation, but need to be wrapped with the DU.
Ive edited this twice, because of an embarassing typo. Tom had the right of it from the start

Related

Is it possible to have F# able to recognize an overlap of DU and use the right one itself?

type GenericResult =
| Ok
| Error of string
type LoginResult =
| Ok
| UserNotFound
| WrongPassword
let check something:GenericResult =
match something with
//| true -> Ok // error:This expression was expected to be of type "GenericREsult" but here has type "LoginResult"
| true -> GenericResult.Ok // I'm forced to specify GenericResult.Ok
| false -> Error "aargg!"
let checkLogin something:LoginResult =
match something with
| true -> Ok // here I don't need to specify the DU because this is defined after
| _ -> WrongPassword
I'd like to use just "Ok" in both the methods, without the need to specify the DU.
I see that in case of clashing of the value the last one is the "predefined".
Ideally I'd like to have a sort of inheritance
to reuse part of a DU in another DU.
For example:
type GenericResult =
| Ok
| Error of string
type LoginResult =
//| GenericResult.Ok
| UserNotFound
| WrongPassword
type SaveResult =
| Created
| Updated
//| GenericResult.Error
let checkLogin something: LoginResult | GenericResult.Ok =
match something with
| true -> Ok
| _ -> WrongPassword
[EDIT]
The real scenario where I feel the need for this feature is this with 3 different results from 3 different logic classes.
There will be in the future more cases so the multiplication of duplicated DU values will increase.
// DUs ordered from the most specific to the most generic
type BalanceUpdateResult =
| Created
| Updated
| InvalidRequest of string
type DeleteResult =
| Ok
| InvalidRequest of string
type Result<'T> =
| Ok of 'T
| NotValid of string
| Error of string
The goal is to have a clean match syntax in the consumer, where the value of the DU will evenctually be used to raise an exception or to return the created value, for example.
// balance update function (result is BalanceUpdateResult):
match result with
| Created -> this.createOkWithStatus 201
| Updated -> this.createOkWithStatus 200
| InvalidRequest error -> this.createErrorForConflict error
// company creation function (result is Result<Company>):
match result with
| Result.Ok newItem ->
context.Logger.Log $"Company created. New Id:{newItem.Id}, Name:{newItem.Name}."
this.createCreated newItem
| NotValid error -> base.createErrorForConflict error
| Error error -> base.createError error
Here, for example, InvalidRequest is not accepted in the second case because it belongs to the wrong DU.
Having to specify the DU everywhere results in a mess like the following example (see the many Result<_>.):
interface ICompanyLogic with
member this.Create(company:Company):Result<Company> =
match normalize company |> validate with
| NotValid msg -> Result<_>.NotValid msg
| Valid validCompany ->
match companyRepository.Exists(validCompany.Name) with
| true -> Result<_>.NotValid($"A company with name \"{validCompany.Name}\" already exists.")
| _ ->
let newCompany = assignNewId validCompany
companyRepository.Create(newCompany)
Result<_>.Ok(newCompany)
member this.Update (company:Company):Result<Company> =
let checkNameExists company =
match companyRepository.GetByName company.Name with
| Some c when c.Id <> company.Id -> NotValid $"A company with name \"{company.Name}\" already exists."
| _ -> Valid company
match normalize company |> validate with
| NotValid msg -> Result<_>.NotValid msg
| Valid c -> match checkNameExists c with
| Valid c -> companyRepository.Update c; Result<_>.Ok c
| NotValid msg -> Result<_>.NotValid msg
I think the best way to achieve what you are trying to do would be to start with a generic Result type that has a type parameter representing the error type:
type Result<'TError> =
| Ok
| Error of 'TError
This allows you to use different types for representing errors, including string, but also another DU to capture more specific error types. You can then define GenericResult and LoginResult as two type aliases:
type LoginError =
| UserNotFound
| WrongPassword
type GenericResult = Result<string>
type LoginResult = Result<LoginError>
To report a login error, you would now use Error WrongPassword to wrap the specific error in the generic Error constructor. The implementation of your two functions looks as follows:
let check something:GenericResult =
match something with
| true -> Ok
| false -> Error "aargg!"
let checkLogin something:LoginResult =
match something with
| true -> Ok
| _ -> Error WrongPassword
Unlike TypeScript union type, F# DU are meant to be composed and not extensible - see Thomas answer for a solution using this approach.
Since F# does not offer a direct solution, you may consider renaming cases like InvalidRequest in order to be more specific and to help differentiate them when reading the code. With these specific names, you can also merge all result types into a big Event DU like what's usually done in an event sourced system:
type Event =
// BalanceUpdateResult
| BalanceCreated
| BalanceUpdated
| BalanceUpdateError of string
// DeleteResult
| DeleteOk
| DeleteError of string
// ...
Ok, as explained by Romain multiple DUs cannot solve my problem.
I decided to use the built-in type Result<'T,'TError>.
It allows me to avoid create many DUs that inevitably will have clash of names, forcing the use the full DU prefix in the code.
I solved the problem that drove me to create custom DUs with the inspiring example from Thomas reply.
(with Result<,>) I have the possibility to have dinstinct Errors or Oks.
(note the Result<unit,_> and the Result<BalanceUpdateRequest,_>)
type ICompanyLogic =
abstract member Create:Company -> Result<Company, string> // CreateResult
abstract member Update:Company -> Result<Company, string> // UpdateResult
abstract member Delete:string -> Result<unit,string> // DeleteResult
type BalanceUpdateResult =
| Created
| Updated
type IBalanceLogic =
abstract member CreateOrUpdate: request:BalanceUpdateRequest -> Result<BalanceUpdateResult, string>
Apart BalanceUpdateResult all the other DUs where replaced buy the Result<'T,'TError>.
I just maintained a couple one for specific tasks:
type CompanyValidation = Valid of Company | NotValid of string
type ValidateResult = Valid | NotValid of string
In the end with this solution:
I don't need to define many DUs
I can customize the Result... within as many values I want (storing a sub-DU in the Ok or Error union case)
I don't need to use prefix or use synonims to avoid clash (code result much cleaner)

F# - Pattern matching discriminated union and accessing object's properties

from F# tour i have this example
type Person = {
First : string
Last : string
}
/// A Discriminated Union of 3 different kinds of employees
type Employee =
| Engineer of engineer: Person
| Manager of manager: Person * reports: List<Employee>
| Executive of executive: Person * reports: List<Employee> * assistant: Employee
let rec findDaveWithOpenPosition(emps: List<Employee>) =
emps
|> List.filter(function
| Manager({First = "Dave"}, []) -> true
| Executive({First = "Dave"}, [], _) -> true
| _ -> false
)
However I would like to get access to object after matching object, something like this:
let rec findDaveWithOpenPos2(emps: List<Employee>) =
List.filter (fun (e:Employee) ->
match e with
| Manager({First = "Dave"}, []) -> e.Last.Contains("X") //Does not compile
| Executive({First = "Dave"}, [], _) -> true
| _ -> false
) emps
So i would like to have statically typed "e" as Person or Employee or Manager variable on right hand side with access to it's properties.
Is it possible? Is there a better construction?
You can name the Person instance within the Manager case using as:
let rec findDaveWithOpenPos2(emps: Employee list) =
List.filter (fun (e:Employee) ->
match e with
| Manager({First = "Dave"} as p, []) -> p.Last.Contains("X")
| Executive({First = "Dave"}, [], _) -> true
| _ -> false
) emps

Discriminated union with an already defined class

I have the School class (with 2 constructors):
type School(name, antiquity) =
member this.Name: string = name
member this.Antiquity: int = antiquity
new(name) = School(name, 0)
And the types of building:
type Building =
| House
| School of School
And I want know what type is a building with the function "knowType":
let knowType building =
match building with
| House -> "A house!"
| School -> "A school" // Error
The error in "knowType" is in the second case: "The constructor is applied to 0 arguments, but expect 1".
It should be
let knowType building =
match building with
| House -> "A house!"
| School _ -> "A school"
You need to give a variable for the of School part. _ just means it is ignored

Active Pattern Matching with Discriminated Unions

Is there any way to use a discriminated union of the following form with active pattern matching? I haven't been able to find any examples.
This is what I'm trying to do:
type c = a | b
type foo =
| bar1
| bar2 of c
//allowed
let (|MatchFoo1|_|) aString =
match aString with
| "abcd" -> Some bar1
| _ -> None
//not allowed
let (|MatchFoo2|_|) aString =
match aString with
| "abcd" -> Some (bar2 of a)
| _ -> None
Why can "Some" not be used in the second way? Is there another way to achieve the same thing?
You only need to use of when declaring the type, so you can just construct values with the bar2 constructor like:
bar2 a
Your second function should work if you change it to:
let (|MatchFoo2|_|) aString =
match aString with
| "abcd" -> Some (bar2 a)
| _ -> None

How to do pattern matching in Rx. Where and Select in a single operator?

Suppose I have this type:
type T = int option
and an observable of that type:
let o : IObservable<T> = // create the observable
I'm looking for a better way to express this:
o.Where(function | None -> false | Some t -> true)
.Select(function | Some t -> t)
An observable that only propagates the Some case.
There are several things that I don't like.
I'm using 2 operators
I'm pattern matching twice
The second pattern matching isn't exhaustive (makes visual studio show a warning and feels odd)
Too much code. The pattern repeats every time I need pattern matching.
Can't you use Observable.choose ? something like this :
let o1 : IObservable<int option> = // ...
let o2 = Observable.choose id o1
If you have a type that is not an option, say:
type TwoSubcases<'a,'b> = | Case1 of 'a | Case2 of 'b
and a partial active pattern:
let (|SecondCase|_|) = function
| Case1 _ -> None
| Case2 b -> Some b
then you can do:
let o1 : IObservable<TwoSubcases<int, float>> = // ...
let o2 : IObservable<float> = Observable.choose (|SecondCase|_|) o1
Thanks to #Lee I came up with a nice solution.
o.SelectMany(function | None -> Observable.Empty() | Some t -> Observable.Return t)
This works for any union type, not only Option.

Resources