While working on a library recently, I came across an elegant use of partial application.
How would we model the first monday in any given month. Firstly I needed a way of passing around the concept of a
month which ends up being simply
fromGregorian applied to a year and month index.
The type signature being:
fromGregorian :: Integer -> Int -> Int -> Day
So, a month, when partially applied, becomes a function taking an
Int and returning a
Day which is exactly what we want.
A common scenario for a bank holiday library is “first monday in month” and with our ability to pass in “months” to a function we define it this way:
= map (fromGregorian 1999) [1,2,3] [jan, feb, mar] = addDays offset (month 07) firstMondayIn month where = negate (weekIndex (month 02)) offset = toModifiedJulianDay day `mod` 7weekIndex day
The most difficult part here was figuring out how Julian time worked, but the solution ends up being super readable.
The other scenario for describing US bank holidays is the case where a holiday falls on a weekend.
= [weekendHolidayFrom (jan 1), weekendHolidayFrom (jan 20)] january_holidays weekendHolidayFrom :: Day -> Maybe Day = case weekIndex d of weekendHolidayFrom d 3 -> Nothing -- saturday 4 -> Just (addDays 1 d) -- sunday -> Just d _
From here, we have a list of
Maybe Day’s so we need to filter them down to
Days. There’s probably a better way of doing this, but I used
mapMaybe because of the type:
(a -> Maybe b) -> [a] -> [b]
The subtle benefit
There is plenty said about the benefits of partial application or the lack thereof, but this isn’t really about that. It’s about the ease of prototyping this sort of solution. From what I can tell, the equivalent ruby solution would not have been as clean, because of the lack of first class partial application.
I’m also amused by this application because most other cases I read have seemingly contrived examples whereas this seems natural to me