There are ways to write things that are similar with Haskell. Just like there are design patterns for functional techniques in imperative languages, there are design patterns for imperative techniques in functional language....
So for example, here you could make a nice little EC2 DSL that would get your stuff (for the actual Haskellers: Free Monad):
spotPrices :: Region -> EC2 [SpotPrice]
spotPrices = GetSpotPrice -- DSL Leaf, executed by runEC2
spotPricesForAllRegions :: [Region] -> EC2 [SpotPrice]
spotPricesForRegions regions =
concat <$> MapM spotPrices regions
Your spot price update function becomes something even simpler now:
runEC2 :: EC2 a -> EC2Config -> IO a
runEC2 = stuff -- this is where you call whatever EC2
-- lib is available
updateSpotPriceFile :: [Region] -> IO [SpotPrice]
updateSpotPriceFile regions = do
spotPrices <- runEC2 (spotPricesForAllRegions regions)
(myEC2Configuration)
writeToFile (toJSON spotPrices) "spotprices.json"
return spotPrices
I'm not going to port all of it, but with a more functional version you could avoid having to keep on making EC2 clients all over the place, and you could keep your config in one place.
The problem is that it's pretty obtuse if you don't know the right design patterns. There's a myth in FP circles that design patterns are for OOP only ("we have functions, we don't need observer patterns!"), but it's a myth. But if you get good, you can write some pretty concise code + get all the goodies.
Haskell has the whole GHC thing, and also the issue of lack of good library documentation (or lack of libraries on certain stuff)...