1
\$\begingroup\$

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

            Console.ReadLine() |> ignore

            0
\$\endgroup\$
3
\$\begingroup\$
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).

\$\endgroup\$
  • \$\begingroup\$ yes, should be receipt, misspelling. \$\endgroup\$ – Alexan May 20 '14 at 13:24
  • \$\begingroup\$ I have 2 files and main in second. So I can remove [<EntryPoint>] if I remove namespace? What do you mean fix open s? \$\endgroup\$ – Alexan May 20 '14 at 13:28
  • 1
    \$\begingroup\$ @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. \$\endgroup\$ – svick May 20 '14 at 15:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.