2

I'm tackling a simple leap year exercise in Haskell, and I'd like to make my solution point-free. Starting from this:

isLeapYear :: Integer -> Bool
isLeapYear year = divisibleBy 400 year || divisibleBy 4 year && not (divisibleBy 100 year)
  where
    divisibleBy m y = (== 0) $ flip mod m y

I tried using liftA3, with a function doing (x || (y && z)) following this, but the tests do not finish and I don't know why.

So, then, I have 3 questions:

  • Do you have any pointers as to how could I achieve this?
  • In my first solution, what's preventing divisibleBy to be point-free? (Typechecker complains if I remove the arguments)
  • As I mentioned before, I tried something like liftA3 (\x y z -> x || (y && z)) (divisibleBy 400) (divisibleBy 4) (indivisibleBy 100), but the tests hang. Why does that happen? I'm not getting how liftA3 works.

Thanks a lot for your help.

DavSanchez
  • 831
  • 8
  • 13

1 Answers1

5

In my first solution, what's preventing divisibleBy to be point-free? (Typechecker complains if I remove the arguments)

You might think that these are equivalent (I am writing flipmod as one function for simplicity's sake):

divisibleBy  m y = (== 0) $ flipmod m y
divisibleBy'     = (== 0) . flipmod

But in actuality, divisibleBy' is now an (invalid) function that takes an argument x and then compares flipmod x to zero:

    ((==0) . flipmod) 5
→   (==0) (flipmod 5)
→   flipmod 5 == 0

Comparing a function (flipmod 5) and a number is certainly no good.

You'd need to write something more sophisticated, namely:

divisibleBy = ((== 0) .) . flipmod

So that now, properly:

    divisibleBy 5 6
→   (((== 0) .) (flipmod 5)) 6
→   ((== 0) . flipmod 5) 6
→   (== 0) (flipmod 5 6)
→   flipmod 5 6 == 0

This construction (f.).g can also be written as ((.).(.)) f g, and that operator is sometimes called dot. I don't think writing things like this is a very good idea, but it might answer your question.


The tests hang. Why does that happen?

I don't know. You'll probably need to provide an mcve here, because this works fine for me as a full program:

import Control.Applicative

isLeapYear :: Integer -> Bool
isLeapYear = liftA3 (\x y z -> x || (y && z))
                    (divisibleBy 400)
                    (divisibleBy 4)
                    (not . divisibleBy 100)
  where
    divisibleBy m y = (== 0) $ flip mod m y

main = print (filter isLeapYear [1850..1950])
Lynn
  • 10,425
  • 43
  • 75
  • Mmm, perhaps there was a character slipping by or something, because I have checked again and it worked. Thanks @Lynn ! – DavSanchez Aug 27 '20 at 01:07