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:
- Get report templates. In this step I query a DB and get some templates for reports
- Update data that the reports are working on. In this step I query some banking services and save our data on our DB
- 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.
-
\$\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– Jamal2015年10月14日 17:01:54 +00:00Commented Oct 14, 2015 at 17:01
1 Answer 1
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.
-
\$\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.– Daniel P.2015年10月15日 09:35:50 +00:00Commented Oct 15, 2015 at 9:35