Welcome to Elm Kata! A series of posts in which we solve Elm exercises of varying difficulty to learn about the language.

The exercise is simple: identify leap years with Elm. So, given a year, determine whether that year is a leap year or not. The rules are as follows:

``````leap years are on:
- every year that is evenly divisible by 4
- except every year that is evenly divisible by 100
- unless the year is also evenly divisible by 400
``````

Let’s start by writing some functions for checking these divisibility rules.

``````divisibleBy4 year = year % 4 == 0
divisibleBy4 year = year % 100 == 0
divisibleBy4 year = year % 400 == 0
``````

Now for the boolean expression…

``````(divisibleBy4 year) && (not (divisibleBy100 year) || (divisibleBy400 year))
``````

Cool. So now we can put the pieces together and build our function. But first, a function signature!

We want a function that takes an integer (`Int`) and returns a boolean (`Bool`):

``````isLeapYear : Int -> Bool
``````

Bam.

Now, we can add a `let-in` expression to create a local function scope for our `divisibleByX` helpers.

``````isLeapYear year =
let
divisibleBy4 y =
y % 4 == 0

divisibleBy100 y =
y % 100 == 0

divisibleBy400 y =
y % 400 == 0
in
(divisibleBy4 year) && (not (divisibleBy100 year) || (divisibleBy400 year))
``````

This could actually be much better.

First off, we can make `divisibleByX` a lot more useful by having it take two arguments rather than one: the number to divide and the number to divide by.

``````divisibleBy n y =
y % n == 0
``````

Nice, but there’s more. One of Elm’s core libraries, “Basics” has a function called `rem` with the following type signature:

``````rem : Int -> Int -> Int
``````

According to the docs, it does this:

``````Find the remainder after dividing one number by another.

e.g. rem 11 4 == 3
``````

Is this exactly what we need? Yes, this is exactly what we need.

``````divisibleBy n y = rem y n == 0
``````

Noice. Time to refactor.

``````isLeapYear : Int -> Bool
isLeapYear y =
let
divisibleBy n y =
rem y n == 0
in
(divisibleBy 4 y) && (not (divisibleBy 100 y) || (divisibleBy 400 y))
``````

That boolean expression looks like it could be simplified. There’s another interesting boolean operator in the Basics package…`xor`.

``````xor : Bool -> Bool -> Bool

The exclusive-or operator. True if exactly one input is True.
``````

Cool beans. Let’s refactor our solution.

If we read our requirements again, this problem actually lends itself quite well to `xor`. We want all years divisible by 4 EXCEPT the ones divisible by 100.

``````xor (divisibleBy 4 year) (divisibleBy 100 year) || (divisibleBy 400 year)
``````

This leaves us with a third iteration of:

``````isLeapYear : Int -> Bool
isLeapYear year =
let
divisibleBy n y =
rem y n == 0
in
xor (divisibleBy 4 year) (divisibleBy 100 year) || (divisibleBy 400 year)
``````

But surely there is a better way…