3
\$\begingroup\$

I've been wanting to try out F# for some real world programming so I decided to try rewriting a program that is being used at work.

It can be reduced to a few steps:

  1. Get report templates. In this step I query a DB and get some templates for reports
  2. Update data that the reports are working on. In this step I query some banking services and save our data on our DB
  3. Generate new reports. In this step I call a stored procedure that generates a new report

I'm trying to do this outside in and have been using Mark Seemann's blog and Scott Wlaschin's guide as a guide and this is what I've got so far:

module Helpers =
let getValuesInListOfOptions list =
    List.choose (fun x -> 
                    match x with
                    | Some value -> Some value
                    | _ -> None ) list
module PaymentVelocityReports =
open System;
open Helpers;

type ReportTemplateName = string

type ReportTemplate = { 
    Id: int
    GroupId: int
    ReportOwnerSSN: string
    Name: ReportTemplateName
    Description: string
    Enabled: bool
    DayOfMonthToRun: int
    DateFrom: DateTime
    DateTo: DateTime option
    UpdateData: bool}

type Claimant = {
    Id: decimal
    Ssn: string
}

type BankingService = {
    Id: decimal
    ClaimantId: decimal
    Bank: string
    Username: string
    Password: string
    Enabled: bool
}

type ClaimantTemplates ={
    ReportTemplates: ReportTemplate list
    Claimant: Claimant
    BankingServices: BankingService list
}


let updateClaimantData saveNewGiros saveNewPayments (dateFrom: DateTime) (dateTo: DateTime) (bankingServices: BankingService list) = 
    List.iter (fun x -> saveNewGiros dateFrom dateTo x) bankingServices
    List.iter (fun x -> saveNewPayments dateFrom dateTo x) bankingServices

let updateData updateClaimantData (claimantTemplates: ClaimantTemplates) : ReportTemplateName list =
    if List.exists (fun x -> x.UpdateData) claimantTemplates.ReportTemplates then
        let minDate = List.map (fun x -> x.DateFrom) claimantTemplates.ReportTemplates |> List.min
        let maxDateWithNone = List.map (fun x -> x.DateTo) claimantTemplates.ReportTemplates |> List.max
        let maxDateWithoutNone = List.map (fun x -> x.DateTo) claimantTemplates.ReportTemplates |> getValuesInListOfOptions |> List.max

        match maxDateWithNone with
            | None -> updateClaimantData minDate DateTime.Today claimantTemplates.BankingServices
            | _ -> updateClaimantData minDate maxDateWithoutNone claimantTemplates.BankingServices

    List.map (fun x -> x.Name) claimantTemplates.ReportTemplates

let getClaimantTemplates data createClaimantTemplate : ClaimantTemplates list = 
    data |> List.map createClaimantTemplate

//The root of the program
let generateReports (getClaimantTemplates: unit -> ClaimantTemplates list) (updateData: ClaimantTemplates -> ReportTemplateName list) (createReport: ReportTemplateName -> unit) =
    getClaimantTemplates () |> List.map updateData |> List.concat |> List.map createReport |> ignore

I'm at the point where the updateClaimantData and getClaimantTemplates require data access and the createReport function that generateReports needs is also a data access function.

I was hoping to get some criticism to see if I'm on the right track. I'm both new to the language and functional languages as well.

\$\endgroup\$
1
  • \$\begingroup\$ The desire to improve code is implied for all questions on this site. Question titles should reflect the purpose of the code, not how you wish to have it reworked. See How to Ask. \$\endgroup\$
    – Jamal
    Oct 14 '15 at 17:01
2
\$\begingroup\$

From a cursory glance, this looks like a good start, but I haven't made a full exegesis of the code.

A couple of observations:

The getValuesInListOfOptions function seems redundant. AFAICT, you can rewrite the function as List.choose id list, in which case you could inline that expression and remove the function.

It seems odd to me that the function with the comment The root of the program is still a higher-order function defined in the same module as all the other functions. I'd expect the root of the program to be something akin to a main function, that only takes command-line string arguments as input, if it takes any arguments at all.

\$\endgroup\$
1
  • \$\begingroup\$ Thanks for the input Mark. It's good to know I'm on the right track. The comment was just to make it clear that where the outermost function of the module was. It will be called from a main function and the other functions composed there as well. \$\endgroup\$
    – Daniel P.
    Oct 15 '15 at 9:35

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.