10
\$\begingroup\$

I'm trying to learn a little bit about functional programming and as my tool I chose F# since I'm a .NET developer and the environment is more natural to me.

In one of my pet projects I'm dealing with dates, so I created a function to get the last date where a given week day occurred. For example, last Tuesday was yesterday (DateTime.Now.AddDays(-1.0)).

#light

module DateTimeExtensions
    open System    

    let rec whenWasLastBasedOnDate (weekDay:DayOfWeek, currentDate:DateTime) = 
        match currentDate with
        | _ when currentDate.DayOfWeek = weekDay -> currentDate
        | _ -> whenWasLastBasedOnDate(weekDay, currentDate.AddDays(-1.0))

    let whenWasLast (weekDay:DayOfWeek) = 
        let currentDate = DateTime.Now
        match currentDate with
        | _ when currentDate.DayOfWeek = weekDay -> currentDate
        | _ -> whenWasLastBasedOnDate(weekDay, currentDate.AddDays(-1.0))    


    let now = DateTime.Now
    let lastSunday = whenWasLast DayOfWeek.Sunday
    let lastMonday = whenWasLast DayOfWeek.Monday
    let lastTuesday = whenWasLast DayOfWeek.Tuesday
    let lastWednesday = whenWasLast DayOfWeek.Wednesday
    let lastThursday = whenWasLast DayOfWeek.Thursday
    let lastFriday = whenWasLast DayOfWeek.Friday
    let lastSaturday = whenWasLast DayOfWeek.Saturday
\$\endgroup\$
11
\$\begingroup\$

The most obvious point to make is that whenWasLast repeats the entire code of whenWasLastBasedOnDate. You can (and should) simply write the whole method by just calling whenWasLastBasedOnDate:

let whenWasLast (weekDay:DayOfWeek) =
    whenWasLastBasedOnDate(weekDay, DateTime.Now)

Another point is that you should write AddDays(-1.0) as AddDays -1.0. In languages with ML-like syntax it is generally discouraged to add redundant parentheses around function arguments because it encourages the misconception that the parentheses are part of the method call syntax.

On a somewhat more subjective note, I think that using if would read nicer than a match without any patterns.


On a more general design note it is generally considered good style in functional programming to use higher order functions to express the code more abstractly. For example instead of saying "Check if the given day was a weekDay. If so, return it, if not repeat with the day before" you could say "Look at the last seven days and return the one who is a weekDay". This is not only more idiomatic functional programming, but also makes it impossible to get into infinite recursion by getting the exit condition wrong. In code it would look like this:

[-6.0..0.0] |> Seq.map currentDate.AddDays |>
    Seq.find (fun d -> d.DayOfWeek = weekDay)
\$\endgroup\$
  • \$\begingroup\$ Thank you so much! This is what I was looking for. The thought I had when created the function was exactly the same as your last one but I could not express it into code. I guess I'm too imperative yet. Refactored as you proposed. Thanks again! \$\endgroup\$ – tucaz Feb 3 '11 at 2:20
  • \$\begingroup\$ Could you please edit the first part of your answer to include the corrected code with 'just whenWasLastBasedOnDate DateTime.Now'? This would help people not familiar with F# to get your idea. Thanks. \$\endgroup\$ – Xavier Nodet Feb 3 '11 at 10:03

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.