Yraglac's transit woes I decided to dive into the world of functional programming since my programming so far has been mostly in C/C++, Java, and Python. I've gone through a couple Haskell tutorials and played around in GHCi so I decided to try my first real program. I decided to try one of the easier problems on Kattis.com, Transit Woes. You can get the full problem description here.

Essentially you are given a list of times that it takes to walk between bus stops, the transit times on the buses, and the intervals at which the buses come to the stops and you need to calculate the total travel time. It was really hard for me to get into a FP mindset, and I kept thinking how easy it would be if I could just write a for loop but I finally got it. I just wanted to get some advice on good FP coding practices, and what kinds of things I should stop doing or start doing so I can get into good habits from the beginning.

import System.IO
import Control.Monad

main = do
    lines &lt;- replicateM 4 getLine
    let inputs = (map . map) read (map words lines) :: [[Integer]]
    let a = inputs !! 0 !! 0 + inputs !! 1 !! 0
    let b = zipWith (+) (tail $ inputs !! 1) (inputs !! 2)
    let c = inputs !! 3
    let finalTime = calcTime a b c
    putStrLn $ resultStr finalTime (inputs !! 0 !! 1)

calcTime :: Integer -&gt; [Integer] -&gt; [Integer] -&gt; Integer
calcTime current travel intervals
        | length travel == 0 = current
        | otherwise = calcTime (firstStop + head travel) (tail travel) (tail intervals)
        where firstStop = head [n*i | n &lt;- [1..], n*i &gt;= current]
              i = head intervals

resultStr :: Integer -&gt; Integer -&gt; String
resultStr a b
        | a &lt;= b = "yes"
        | otherwise = "no"
You have the right idea here by replacing an imperative loop with recursion. 

The main improvement I would suggest is that in firstStop you are simulating waiting for the bus i minutes at a time instead of calculating how long to wait. The formula I came up with for that is (renaming i to interval as below):

where
  remainder = current `rem` interval
  firstStop =
    case remainder of
        0 -&gt; remainder
        x -&gt; interval - remainder


e.g. if we're at time 26 and interval = 5 then remainder is 1 and interval - remainder is 4 which correct since we need to wait until time 30. The exception is for when the wait time is 0, in which case interval - remainder would tell us 5 instead of 0, so I wrote out a case expression for that.



There are some Haskell practices you could do better. For example, head and tail are discouraged when you can use pattern matching instead. Here's one refactoring you could do:

calcTime :: Integer -&gt; [Integer] -&gt; [Integer] -&gt; Integer
calcTime current [] intervals = current
calcTime current (travelTime:travelTimes) (interval:intervals) =
    calcTime (firstStop + travelTime) travelTimes intervals
        where firstStop = ...