# SalesTax challenge

This is the same problem, but I decided to do it using F#. I'm just interested in how I can improve it.

namespace SalexTaxes

module Product =

type ProductType =
|food = 1
|book = 2
|medical = 3
|other = 4

type Product =
{Name : string;
Type : ProductType;
Price : decimal;
IsImport: bool}

let Tax rate price = (price : decimal) * rate
let BasicTax = Tax 0.1m
let ImportDuty = Tax 0.05m

type ShoppingCartItem =
{Item : Product;
Quantity: int}
member x.BasicTaxes =
match x.Item.Type with
| ProductType.food | ProductType.book | ProductType.medical -> 0.0M
| _ -> BasicTax(x.Item.Price)
member x.ImportDuty =
match x.Item.IsImport with
| true -> ImportDuty(x.Item.Price + x.BasicTaxes)
| _ -> 0.0M

module Order =

let price (recit : List<ShoppingCartItem>)  =
recit |> List.sumBy (fun x -> x.Item.Price)

let taxes (recit : List<ShoppingCartItem>)  =
recit |> List.sumBy (fun x -> (x.BasicTaxes + x.ImportDuty))

let PrintRecit recit =
recit |> List.iter (fun x -> printf "%s: %M\n" x.Item.Name (System.Math.Round((x.Item.Price + x.BasicTaxes + x.ImportDuty),2)))
let totaltax = System.Math.Round((taxes recit),2)
let total1 = (price recit) + (taxes recit)
let total = System.Math.Round(total1,2)
printf "Total Taxes %M\n"  totaltax
printf "Total %M\n"  total

namespace SalexTaxes
open System
open Product
open SalexTaxes

module Program =

[<EntryPoint>]
let main (argv :string[]) =

let prod1 = {Name = "book"; Type = ProductType.book; Price = 12.49m; IsImport = false}
let item1 = {Item = prod1; Quantity = 1}
let prod2 = {Name = "music CD"; Type = ProductType.other; Price = 14.99m; IsImport = false}
let item2 = {Item = prod2; Quantity = 1}
let prod3 = {Name = "chocolate bar"; Type = ProductType.food; Price = 0.85m; IsImport = false}
let item3 = {Item = prod3; Quantity = 1}

let order =  [item1; item2; item3]

Order.PrintRecit order

0


type ProductType =
|food = 1
|book = 2
|medical = 3
|other = 4


This is how you create an enum in F#. But a more idiomatic way would be to create a discriminated union:

type ProductType =
| Food
| Book
| Medical
| Other


This will also mean that you won't wrote ProductType.food to use it, but just Food.

let Tax rate price = (price : decimal) * rate


let functions are usually named in lower case. And you don't need to specify the type of price here, it will be inferred from usage:

let tax rate price = price * rate


But if you want to specify the type of the parameter, I think it's more common to do that on the parameter side:

let tax rate (price : decimal) = price * rate


BasicTax(x.Item.Price)


There is no reason for those parentheses here, a more common way to write this would be:

basicTax x.Item.Price


0.0M


A shorter way to write that is just 0M.

match x.Item.IsImport with
| true -> ImportDuty(x.Item.Price + x.BasicTaxes)
| _ -> 0.0M


Instead of pattern matching on a bool, you can use if:

if x.Item.IsImport
then ImportDuty(x.Item.Price + x.BasicTaxes)
else 0.0M


let price (recit : List<ShoppingCartItem>)  =


What does recit mean? Is it French? A typo for receipt? Or some abbreviation?

Also, List is usually spelled list.

module Program =

[<EntryPoint>]
let main (argv :string[]) =


You can avoid all this boilerplate code. The code of main can be directly at the top level of the file, if you remove the namespace declaration (and fix your opens).

• yes, should be receipt, misspelling. – Alexan May 20 '14 at 13:24
• I have 2 files and main in second. So I can remove [<EntryPoint>] if I remove namespace? What do you mean fix open s? – Alexan May 20 '14 at 13:28
• @Alex You open Product before SalesTaxes. With the namespace, opening SalesTaxes is unnecessary. If you remove the namespace, you will have to correct the order of the two opens. – svick May 20 '14 at 15:04