How to categorize over units of measure? - f#

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.

Related

How to model an entity with a currency?

I am trying to model a bond entity in F# using a unit of measure for the currency.
type Bond = {
Isin: string
Issuer: string
Maturity: DateTime
Price: float<???>
}
Let's assume that a static table with all the available/possible currencies is available.
type Currency = {
Code : string
Name : string
}
I can go for Price as float and PriceCurrency as string or even as a Currency type but I think that this is not ideal.
Any ideas?
I don't think F# units of measure are a good match for this use case, since the compiler isn't aware of the currency table. If you want to use units of measure anyway, each currency would have to be hard-coded into your source, like this:
open System
[<Measure>] type Dollar
[<Measure>] type Pound
type Bond =
{
Isin: string
Issuer: string
Maturity: DateTime
}
type DollarBond =
{
Bond: Bond
Price: float<Dollar>
}
type PoundBond =
{
Bond: Bond
Price: float<Pound>
}
let poundsPerDollar = 0.73<Pound/Dollar>
let toPoundBond (dollarBond : DollarBond) =
{
Bond = dollarBond.Bond
Price = dollarBond.Price * poundsPerDollar
}
let dollarBond : DollarBond =
{
Bond = {
Isin = "My isin"
Issuer = "My issuer"
Maturity = DateTime.Parse("1/1/2050")
}
Price = 1000.0<Dollar>
}
printfn "%A" <| toPoundBond dollarBond

How to find sum for each category in an array?

I have an array that contains multiple items. This is a Realm array. It contains data in the following structure:
Results<Entry> <0x7ff04840e1a0> (
[0] Entry {
name = Salary;
amount = 40000;
date = 2020-03-18 16:00:00 +0000;
category = Main Incomes;
entryType = Income;
},
[1] Entry {
name = Diff;
amount = -500;
date = 2020-04-18 16:00:00 +0000;
category = Misc;
entryType = Expense;
},
[2] Entry {
name = Cat Food;
amount = -399;
date = 2020-04-18 16:00:00 +0000;
category = Animals;
entryType = Expense;
},
[3] Entry {
name = Fish Food;
amount = -599;
date = 2020-04-18 16:00:00 +0000;
category = Animals;
entryType = Expense;
}
)
What I am trying to achieve is to make another array that will 'pivot' totals for each category. So it can work as a pivot table in Excel.
The desired output is an array that will contain totals for each category:
[0] X-Array {
category = Main Incomes;
amount = XXXX;
},
[1] X-Array {
category = Animals;
amount = XXXX;
And so on...
I'm fairly new to this fancy one-liners like .map and .reduce and other Swift's array management sugar, so would very much appreciate the advice!
Thank you!
P.S. I plan to do the same thing with total expenses and incomes in order to calculate closing balance.
I think use Dictionary is easier to do categorize and later you can convert it to array or anything you want
var dict:[String:Double] = [:]
list.forEach {
if let current = dict[$0.category]{
dict[$0.category] = current + $0.amount
}else{
dict[$0.category] = $0.amount
}
}
print(dict)
I think the question is asking how to get the sum for each category, Income, Expense etc. If so, here's how it's done using the .sum function on the results.
let totalIncome = realm.objects(AccountClass.self)
.filter("entryType == 'Income'")
.sum(ofProperty: "amount") as Int
let totalExpense = realm.objects(AccountClass.self)
.filter("entryType == 'Expense'")
.sum(ofProperty: "amount") as Int
print("Total Income: \(totalIncome)")
print("Total Expense: \(totalExpense)")
and the output for your example data is
Total Income: 40000
Total Expense: 1498
If you want a pivot table, you could just add the amounts to an array - if you need the labels, use a tuple to store them in an array like this
let i = ("Income", totalIncome)
let e = ("Expense", totalExpense)
let tupleArray = [i, e]
for account in tupleArray {
print(account.0, account.1)
}
Keep in mind that a Results object is not an array but has some array-like functions. Results are live updating so as the underlying data in Realm changes, the Results change along with it.

Realm - return array list

I have a realm object called mood and I'm trying to return all values in the field activities as a [String] list where mood = great
My Realm object looks like:
Results<Mood> <0x7fce164851a0> (
[0] Mood {
mood = Great;
comment = test;
activities = Friends;
time = 8:06 PM;
date = 4/10/18;
symptom = down;
},
[1] Mood {
mood = Sad;
comment = ;
activities = Date;
time = 6:22 PM;
date = 4/14/18;
symptom = up;
},
[2] Mood {
mood = Sad;
comment = ;
activities = Date;
time = 7:40 PM;
date = 4/14/18;
symptom = Night;
},
[3] Mood {
mood = Sad;
comment = ;
activities = Date;
time = 7:41 PM;
date = 4/14/18;
symptom = Dry;
},
[4] Mood {
mood = Awful;
comment = this is an example data for the app!;
activities = Date;
time = 2:17 PM;
date = 4/15/18;
symptom = Lost;
}
)
and I want to return a list like
["down"]
because thats the activity where mood = great etc
I have tried:
func getGreatMoodActivity() -> [String]{
let greaMoodActivity = Set(realm.objects(Mood.self).filter("mood = 'Great'") as! [String])
return getGreatMoodActivity()
}
But this crashes with this error
Could not cast value of type 'RealmSwift.Results'
(0x10f520810) to 'NSArray' (0x10ce35008).
Is there a way to fix this?
The filter operation will keep filter the Mood objects matching your given criteria, so its return value will be of type Results<Mood> which you obviously can't convert in coercion to Array<String>.
It seems that you are actually trying to map the Mood objects matching your filter criteria to their symptom property, which is of type String. To achieve your goals, you need to call map after filtering the Mood objects.
Moreover, your return value is completely flawed, you want to return the array stored in the variable greatMoodActivity and not call your function recursively.
func getGreatMoodActivity() -> [String]{
let greatMoodActivity = realm.objects(Mood.self).filter("mood = 'Great'").map({$0.symptom})
return Array(greatMoodActivity)
}

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