How to deconstruct union with "with"? - f#

I have some problems to use "with" with a discriminated union:
type NaturalPerson = {
FirstName: string
LastName: string
}
type CorporateEntity = {
Name1: string
Name2: string option
}
type Person =
| Natural of NaturalPerson
| Company of CorporateEntity
let company = Company { Name1 = "Foo Bar AG"; Name2 = Some "Baz" }
Now I want to change Name2 to None, but I could not figure out how.
Something like:
let company2 = Company { company with Name2 = None }
In my "real world example" of course this is nested, otherwise I could use the correct type.
Maybe this isn't possible, because I have to pattern match for an edge case, that can not exist (but the compiler is not smart enough to know).

If you break it out a bit more it is easier to see the problem. In fact what is probably making this difficult is the naming.
let company = Company { Name1 = "Foo Bar AG"; Name2 = Some "Baz" } // Person
let company2 = Company { company with Name2 = None } // Person, but broken because expecting company to be type of CorporateEntity
So you are trying to create a CorporateEntity with a Person type, which are not the same.
This works because the correct type is used.
let c1 : CorporateEntity = { Name1 = "Foo Bar AG"; Name2 = Some "Baz" }
let p1 : Person = Company c1
let c2 : CorporateEntity = { c1 with Name2 = None }
let p2 : Person = Company c2
I have added the types and changed the name to make the type more apparent.
You could match on this...
match company with // <- rename company to person so it is clearer
| Natural _ -> company
| Company c -> Company { c with Name2 = None }
If you wanted to match in a function you could do it like this:
let noCompanyName2 (c:CorporateEntity) = // c:CorporateEntity -> Person
let { Name1 = n1; Name2 = _ } = c
let company3 = Company { Name1 = n1; Name2 = None }
company3
Or more concisely:
let noCompanyName2 ({ Name1 = n1; Name2 = _ }) = Company { Name1 = n1; Name2 = None }
Hope this helps.

Here's how you would do it (I'm assuming that if the variable company is a NaturalPerson then you want it unchanged):
match company with
| Person _ -> company
| Company corpEntity -> Company { corpEntity with Name2 = None }

Related

Computation Expression for constructing complex object graph

Given the following types:
type Trip = {
From: string
To: string
}
type Passenger = {
Name: string
LastName: string
Trips: Trip list
}
I'm using the following builders:
type PassengerBuilder() =
member this.Yield(_) = Passenger.Empty
[<CustomOperation("lastName")>]
member __.LastName(r: Passenger, lastName: string) =
{ r with LastName = lastName }
[<CustomOperation("name")>]
member __.Name(r: Passenger, name: string) =
{ r with Name = name }
type TripBuilder() =
member __.Yield(_) = Trip.Empty
[<CustomOperation("from")>]
member __.From(t: Trip, f: string) =
{ t with From = f }
// ... and so on
to create records of type Passenger, like so:
let passenger = PassengerBuilder()
let trip = TripBuilder()
let p = passenger {
name "john"
lastName "doe"
}
let t = trip {
from "Buenos Aires"
to "Madrid"
}
how would I go about combining the PassengerBuilder and the TripBuilder so that I can achieve this usage?
let p = passenger {
name "John"
lastName "Doe"
trip from "Buenos Aires" to "Madrid"
trip from "Madrid" to "Paris"
}
that returns a Passenger record like:
{
LastName = "Doe"
Name = "John"
Trips = [
{ From = "Buenos Aires"; To = "Madrid" }
{ From = "Madrid"; To = "Paris" }
]
}
Is there any reason why you want to use computation expression builder? Based on your example, it does not look like you're writing anything computation-like. If you just want a nice DSL for creating trips, then you could quite easily define something that lets you write:
let p =
passenger [
name "John"
lastName "Doe"
trip from "Buenos Aires" towards "Madrid"
trip from "Madrid" towards "Paris"
]
This is pretty much exactly what you asked for, except that it uses [ .. ] instead of { .. } (because it creates a list of transformations). I also renamed to to towards because to is a keyword and you cannot redefine it.
The code for this is quite easy to write and follow:
let passenger ops =
ops |> List.fold (fun ps op -> op ps)
{ Name = ""; LastName = ""; Trips = [] }
let trip op1 arg1 op2 arg2 ps =
let trip =
[op1 arg1; op2 arg2] |> List.fold (fun tr op -> op tr)
{ From = ""; To = "" }
{ ps with Trips = trip :: ps.Trips }
let name n ps = { ps with Name = n }
let lastName n ps = { ps with LastName = n }
let from n tp = { tp with From = n }
let towards n tp = { tp with To = n }
That said, I would still consider using normal F# record syntax - it is not that much uglier than this. The one drawback of the version above is that you can create passengers with empty names and last names, which is one thing that F# prevents you from!
I'm not sure this is what you wanted, but nothing prevents you from creating a new operation called trip on your PassengerBuilder:
[<CustomOperation("trip")>]
member __.Trip(r: Passenger, t: Trip) =
{ r with Trips = t :: r.Trips }
and then using it like this:
let p = passenger {
name "John"
lastName "Doe"
trip (trip { from "Buenos Aires"; to "Madrid" })
trip (trip { from "Madrid"; to "Paris" })
}
Arguably, you can even make it cleaner by dropping the TripBuilder altogether:
let p = passenger {
name "John"
lastName "Doe"
trip { From = "Buenos Aires"; To = "Madrid" }
trip { From = "Madrid"; To = "Paris" }
}
If this is somehow not what you wanted, then please specify how. That is, what is missing or what is extra in this solution.

How to categorize over units of measure?

The problem is simple, I wish to do some calculations on some travel expenses which include both expenses in DKK and JPY. Thus I've found a nice way to model currency so I am able to convert back and forth:
[<Measure>] type JPY
[<Measure>] type DKK
type CurrencyRate<[<Measure>]'u, [<Measure>]'v> =
{ Rate: decimal<'u/'v>; Date: System.DateTime}
let sep10 = System.DateTime(2015,9,10)
let DKK_TO_JPY : CurrencyRate<JPY,DKK> =
{ Rate = (1773.65m<JPY> / 100m<DKK>); Date = sep10}
let JPY_TO_DKK : CurrencyRate<DKK,JPY> =
{ Rate = (5.36m<DKK> / 100.0m<JPY>); Date=sep10 }
I proceed to model expenses as a record type
type Expense<[<Measure>] 'a> = {
name: string
quantity: int
amount: decimal<'a>
}
and here I have an example list of expenses:
let travel_expenses = [
{ name = "flight tickets"; quantity = 1; amount = 5000m<DKK> }
{ name = "shinkansen ->"; quantity = 1; amount = 10000m<JPY> }
{ name = "shinkansen <-"; quantity = 1; amount = 10000m<JPY> }
]
And this is where the show stops... F# doesn't like that list, and complaints that all of the list should be DKK, -which of course makes sense.
Then I thought that there must be some smart way to make a discriminated union of my units of measures to put them in a category, and then I attempted with:
[<Measure>] type Currency = JPY | DKK
But this is not possible and results in The kind of the type specified by its attributes does not match the kind implied by its definition.
The solution I've come up with so far is very redundant, and I feel that it makes the unit of measure quite pointless.
type Money =
| DKK of decimal<DKK>
| JPY of decimal<JPY>
type Expense = {
name: string
quantity: int
amount: Money
}
let travel_expenses = [
{ name = "flight tickets"; quantity = 1; amount = DKK(5000m<DKK>) }
{ name = "shinkansen ->"; quantity = 1; amount = JPY(10000m<JPY>) }
{ name = "shinkansen <-"; quantity = 1; amount = JPY(10000m<JPY>) }
]
Is there a good way of working with these units of measures as categories? like for example
[<Measure>] Length = Meter | Feet
[<Measure>] Currency = JPY | DKK | USD
or should I remodel my problem and maybe not use units of measure?
Regarding the first question no, you can't but I think you don't need units of measures for that problem as you state in your second question.
Think how do you plan to get those records at runtime (user input, from a db, from a file, ...) and remember units of measures are a compile-time features, erased at runtime. Unless those records are always hardcoded, which will make your program useless.
My feeling is that you need to deal at run-time with those currencies and makes more sense to treat them as data.
Try for instance adding a field to Expense called currency:
type Expense = {
name: string
quantity: int
amount: decimal
currency: Currency
}
then
type CurrencyRate = {
currencyFrom: Currency
currencyTo: Currency
rate: decimal
date: System.DateTime}
As an alternative to Gustavo's accepted answer, If you still want to prevent anybody and any function accidentally summing JPY with DKK amounts, you can keep your idea of discriminated union like so :
let sep10 = System.DateTime(2015,9,10)
type Money =
| DKK of decimal
| JPY of decimal
type Expense = {
name: string
quantity: int
amount: Money
date : System.DateTime
}
type RatesTime = { JPY_TO_DKK : decimal ; DKK_TO_JPY : decimal ; Date : System.DateTime}
let rates_sep10Tosep12 = [
{ JPY_TO_DKK = 1773.65m ; DKK_TO_JPY = 5.36m ; Date = sep10}
{ JPY_TO_DKK = 1779.42m ; DKK_TO_JPY = 5.31m ; Date = sep10.AddDays(1.0)}
{ JPY_TO_DKK = 1776.07m ; DKK_TO_JPY = 5.33m ; Date = sep10.AddDays(2.0)}
]
let travel_expenses = [
{ name = "flight tickets"; quantity = 1; amount = DKK 5000m; date =sep10 }
{ name = "shinkansen ->"; quantity = 1; amount = JPY 10000m; date = sep10.AddDays(1.0)}
{ name = "shinkansen <-"; quantity = 1; amount = JPY 10000m ; date = sep10.AddDays(2.0)}
]
let IN_DKK (rt : RatesTime list) (e : Expense) =
let {name= _ ;quantity = _ ;amount = a ;date = d} = e
match a with
|DKK x -> x
|JPY y ->
let rtOfDate = List.tryFind (fun (x:RatesTime) -> x.Date = d) rt
match rtOfDate with
| Some r -> y * r.JPY_TO_DKK
| None -> failwith "no rate for period %A" d
let total_expenses_IN_DKK =
travel_expenses
|> List.fold(fun acc e -> (IN_DKK rates_sep10Tosep12 e) + acc) 0m
Even better would be to make function IN_DKK as a member of type Expense and put a restriction (private,...) on the field "amount".
Your initial idea of units of measure makes sense to prevent summing different currencies but unfortunately it does not prevent from converting from one to another and back to the first currency. And since your rates are not inverse (r * r' <> 1 as your data shows), unit of measure for currencies are dangerous and error prone. Note : I did not take into account the field "quantity" in my snippet.

F# - Can I return a discriminated union from a function

I have the following types:
type GoodResource = {
Id:int;
Field1:string }
type ErrorResource = {
StatusCode:int;
Description:string }
I have the following discriminated union:
type ProcessingResult =
| Good of GoodResource
| Error of ErrorResource
Then want to have a function that will have a return type of the discriminated union ProcessingResult:
let SampleProcessingFunction value =
match value with
| "GoodScenario" -> { Id = 123; Field1 = "field1data" }
| _ -> { StatusCode = 456; Description = "desc" }
Is what I am trying to do possible. The compiler is giving out stating that it expects GoodResource to be the return type. What am I missing or am I completely going about this the wrong way?
As it stands, SampleProcessingFunction returns two different types for each branch.
To return the same type, you need to create a DU (which you did) but also specify the case of the DU explicitly, like this:
let SampleProcessingFunction value =
match value with
| "GoodScenario" -> Good { Id = 123; Field1 = "field1data" }
| _ -> Error { StatusCode = 456; Description = "desc" }
You might ask "why can't the compiler figure out the correct case automatically", but what happens if your DU has two cases of the same type? For example:
type GoodOrError =
| Good of string
| Error of string
In the example below, the compiler cannot determine which case you mean:
let ReturnGoodOrError value =
match value with
| "GoodScenario" -> "Goodness"
| _ -> "Badness"
So again you need to use the constructor for the case you want:
let ReturnGoodOrError value =
match value with
| "GoodScenario" -> Good "Goodness"
| _ -> Error "Badness"
You have to state the case of the union type you want to return in either branch.
let SampleProcessingFunction value =
match value with
| "GoodScenario" -> { Id = 123; Field1 = "field1data" } |> Good
| _ -> { StatusCode = 456; Description = "desc" } |> Error
I suggest to read this excellent articles by Scott Wlaschin Railway Oriented Programming
{ Id = 123; Field1 = "field1data" } is a value of type GoodResource, not of type ProcessingResult. To create a value of type ProcessingResult, you need to use one of its two constructors: Good or Error.
So your function can be written like this:
let SampleProcessingFunction value =
match value with
| "GoodScenario" -> Good { Id = 123; Field1 = "field1data" }
| _ -> Error { StatusCode = 456; Description = "desc" }

Subtracting Records from a Set using case-insensitive comparison

I have a set of records:
type Person =
{
Name : string
Age : int
}
let oldPeople =
set [ { Name = "The Doctor"; Age = 1500 };
{ Name = "Yoda"; Age = 900 } ]
Unlike the hardcoded example above, the set of data actually comes from a data source (over which I have very little control). Now I need to subtract a set of data from another data source. In general, the data in this second source matches, but occasionally there is a difference in captialization:
let peopleWhoAreConfusedAboutTheirAge =
set [ { Name = "THE DOCTOR"; Age = 1500 } ]
When I attempt to subtract the second set from the first, it fails because the string comparison is case sensitive:
let peopleWhoKnowHowOldTheyAre =
oldPeople - peopleWhoAreConfusedAboutTheirAge
val peopleWhoKnowHowOldTheyAre : Set<Person> =
set [{Name = "The Doctor";
Age = 1500;}; {Name = "Yoda";
Age = 900;}]
Is there a way to perform a case-insensitive comparison for the Name field of the People record?
This is what I've implemented so far, though there may be a better way to do it.
My solution was to override the Equals function on the People record so as to perform a case-insensitive comparison. Set subtraction uses the Equals function to determine if two records match one another. By overriding Equals, I was forced (via warning and error) to override GetHashCode and implement IComparable (as well as set the CustomEquality and CustomComparison attributes):
[<CustomEquality; CustomComparison>]
type Person =
{
Name : string
Age : int
}
member private this._internalId =
this.Name.ToLower() + this.Age.ToString()
interface System.IComparable with
member this.CompareTo obj =
let other : Person = downcast obj
this._internalId.CompareTo( other._internalId )
override this.Equals( other ) =
match other with
| :? Person as other ->
System.String.Compare( this._internalId, other._internalId ) = 0
| _ -> false
override this.GetHashCode() =
this._internalId.GetHashCode()
This, however, seems to do the trick:
let oldPeople =
set [ { Name = "The Doctor"; Age = 1500 };
{ Name = "Yoda"; Age = 900 } ]
let peopleWhoAreConfusedAboutTheirAge =
set [ { Name = "THE DOCTOR"; Age = 1500 } ]
let peopleWhoKnowHowOldTheyAre =
oldPeople - peopleWhoAreConfusedAboutTheirAge
val peopleWhoKnowHowOldTheyAre : Set<Person> = set [{Name = "Yoda";
Age = 900;}]
If you know a better solution (involving less code), please post it rather than comment on this answer. I will happily accept a less verbose, awkward solution.
Here's another approach:
type Name(value) =
member val Value = value
override this.Equals(that) =
match that with
| :? Name as name -> StringComparer.CurrentCultureIgnoreCase.Equals(this.Value, name.Value)
| _ -> false
override this.GetHashCode() =
StringComparer.CurrentCultureIgnoreCase.GetHashCode(this.Value)
type Person =
{
Name: Name
Age: int
}
{Name=Name("John"); Age=21} = {Name=Name("john"); Age=21} //true

F# Records: Dangerous, only for limited use, or well used functionality?

So have gotten to record in my F# journey and at first they seem rather dangerous. At first this seemed clever:
type Card = { Name : string;
Phone : string;
Ok : bool }
let cardA = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
The idea that the cardA is patten matched with Card. Not to mention the simplified pattern matching here:
let withTrueOk =
list
|> Seq.filter
(function
| { Ok = true} -> true
| _ -> false
)
Problem is:
type Card = { Name : string;
Phone : string;
Ok : bool }
type CardTwo = { Name : string;
Phone : string;
Ok : bool }
let cardA = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
cardA is now of CardTwo type which I am guessing has to do with F# running everything in order.
Now this might be an impossible situation since there may never be a chance of the same signature taking on two type, but it is a possibility.
Is recording something that has only limited use or am I just over thinking this one?
They are not dangerous and they are not only for limited use.
I think it's very rare that you would have two types with the same members. But if you do encounter that situation, you can qualify the record type you want to use:
let cardA = { Card.Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
Records are very useful for creating (mostly) immutable data structures. And the fact that you can easily create a copy with just some fields changed is great too:
let cardB = { cardA with Ok = true }
I agree, record fields as members of the enclosing module/namespace seems odd at first coming from more traditional OO languages. But F# provides a fair amount of flexibility here. I think you'll find only contrived circumstances cause problems, such as two records that
are identical
have a subset/superset relationship
The first case should never happen. The latter could be solved by record B having a field of record A.
You only need one field to be different for the two to be distinguishable. Other than that, the definitions can be the same.
type Card =
{ Name : string
Phone: string
Ok : bool }
type CardTwo =
{ Name : string
Phone: string
Age : int }
let card = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
let cardTwo = { Name = "Alf" ; Phone = "(206) 555-0157" ; Age = 21 }
Pattern matching is also quite flexible as you only need to match on enough fields to distinguish it from other types.
let readCard card =
match card with
| { Ok = false } -> () //OK
| { Age = 21 } -> () //ERROR: 'card' already inferred as Card, but pattern implies CardTwo
Incidentally, your scenario is easily fixed with a type annotation:
let cardA : Card = { Name = "Alf" ; Phone = "(206) 555-0157" ; Ok = false }
In order that you appreciate what F# provides, I just want to mention that there is no fully qualified accessor for records in OCaml. Therefore, to distinguish between record types with the same fields, you have to put them into submodules and reference them using module prefixes.
So your situation in F# is much better. Any ambiguity between similar record types could be resolved quickly using record accessor:
type Card = { Name: string;
Phone: string;
Ok: bool }
type CardSmall = { Address: string;
Ok: bool }
let withTrueOk list =
list
|> Seq.filter (function
| { Card.Ok = true} -> true (* ambiguity could happen here *)
| _ -> false)
Moreover, F# record is not limited at all. It provides a lot of nice features out-of-the-box including pattern matching, default immutability, structural equality, etc.

Resources