patternMinor
Leap year check in Haskell, using pattern matching or bind operator
Viewed 0 times
yearoperatorleapusinghaskellbindcheckpatternmatching
Problem
One of the first Haskell puzzles on http://exercism.io/ is to implement a leap year check. I've done this twice.
Using pattern matching:
Using the bind operator
I'd like to know which implementation is “better“ / more idiomatic in Haskell. I am unsure whether I might have misused a t0o powerful concept in the second try, and the first try might just be more readable.
Using pattern matching:
isLeapYear :: Integer -> Bool
isLeapYear year
| year `mod` 400 == 0 = True
| year `mod` 100 == 0 = False
| year `mod` 4 == 0 = True
| otherwise = FalseUsing the bind operator
>>=:isLeapYear :: Integer -> Bool
isLeapYear year = head $
[(400, True), (100, False), (4, True), (1, False)]
>>= check year
where check y (interval, isLeap) = [isLeap | y `mod` interval == 0]I'd like to know which implementation is “better“ / more idiomatic in Haskell. I am unsure whether I might have misused a t0o powerful concept in the second try, and the first try might just be more readable.
Solution
The first one is a lot more readable, whereas the second one uses a "hack". I would go with the first one, except that I would use
That being said, for a programming challenge, your version is perfectly fine:
The latter can be rewritten without
You could get rid of the "hack" with
If you really want to use
Note that this shows perfectly that
Which is easier to grasp than the version with
rem, which is a little bit faster. And one could introduce some DRY:isDivisibleBy :: Integral n => n -> n -> Bool
isDivisibleBy x n = x `rem` n == 0
isLeapYear :: Integer -> Bool
isLeapYear year
| divBy 400 = True
| divBy 100 = False
| divBy 4 = True
| otherwise = False
where
divBy n = year `isDivisibleBy` nThat being said, for a programming challenge, your version is perfectly fine:
isLeapYear :: Integer -> Bool
isLeapYear year
| year `rem` 400 == 0 = True
| year `rem` 100 == 0 = False
| year `rem` 4 == 0 = True
| otherwise = FalseThe latter can be rewritten without
>>= as list comprehension:isLeapYear :: Integer -> Bool
isLeapYear year = head [isLeap | (interval, isLeap) <- classifications
, year `isDivisibleBy` interval]
where
classifications = [(400, True), (100, False), (4, True), (1, False)]You could get rid of the "hack" with
safeHead and maybe False, but that's left as an exercise.If you really want to use
check, remove the y. It just introduces an additional error source:isLeapYear :: Integer -> Bool
isLeapYear year = head $
[(400, True), (100, False), (4, True), (1, False)]
>>= check
where check (interval, isLeap) = [isLeap | year `rem` interval == 0]Note that this shows perfectly that
>>= is just flip concatMap for lists. So let's take advantage:isLeapYear :: Integer -> Bool
isLeapYear year = head $ concatMap check classifications ++ [False]
where
check (interval, isLeap) = [isLeap | year `rem` interval == 0]
classifications = [(400, True), (100, False), (4, True)]Which is easier to grasp than the version with
>>=.Code Snippets
isDivisibleBy :: Integral n => n -> n -> Bool
isDivisibleBy x n = x `rem` n == 0
isLeapYear :: Integer -> Bool
isLeapYear year
| divBy 400 = True
| divBy 100 = False
| divBy 4 = True
| otherwise = False
where
divBy n = year `isDivisibleBy` nisLeapYear :: Integer -> Bool
isLeapYear year
| year `rem` 400 == 0 = True
| year `rem` 100 == 0 = False
| year `rem` 4 == 0 = True
| otherwise = FalseisLeapYear :: Integer -> Bool
isLeapYear year = head [isLeap | (interval, isLeap) <- classifications
, year `isDivisibleBy` interval]
where
classifications = [(400, True), (100, False), (4, True), (1, False)]isLeapYear :: Integer -> Bool
isLeapYear year = head $
[(400, True), (100, False), (4, True), (1, False)]
>>= check
where check (interval, isLeap) = [isLeap | year `rem` interval == 0]isLeapYear :: Integer -> Bool
isLeapYear year = head $ concatMap check classifications ++ [False]
where
check (interval, isLeap) = [isLeap | year `rem` interval == 0]
classifications = [(400, True), (100, False), (4, True)]Context
StackExchange Code Review Q#160749, answer score: 6
Revisions (0)
No revisions yet.