Created
December 19, 2018 22:44
-
-
Save StevenXL/b6a5ebfd1e2198b2f5351bbdf3c5ace9 to your computer and use it in GitHub Desktop.
On over-engineered example of FizzBuzz in Haskell
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- This is an over-engineered example of using the `Either a` monad to implement | |
-- a FizzBuzz. This exercise was inspired by "The Power of Composition" by Scott | |
-- Wlaschin (https://youtu.be/WhEkBCWpDas?t=2489). | |
module FizzBuzz where | |
main :: IO () | |
main = mapM_ printAsFizzBuzz [1..100] | |
where printAsFizzBuzz :: Int -> IO () | |
printAsFizzBuzz = putStrLn . fizzBuzz | |
fizzBuzz :: Int -> String | |
fizzBuzz i = either id show $ computeFizzBuzz i | |
-- The key here is to realize that in the Either monad, if the value on the left | |
-- side of the bind is tagged with the Left data constructor, then | |
-- evaluation stops; if the value is instead tagged with the Right data | |
-- constructor, evaluation continues with that value in scope: | |
-- Left v >>= _ = Left v | |
-- Right v >>= f = f v | |
-- Therefore, if we return a value tagged with Left, the rest of the computation | |
-- will be short-circuited. | |
computeFizzBuzz :: Int -> Either String Int | |
computeFizzBuzz i = handle15 >> handle5 >> handle3 >> notDivisible | |
where handle15 = if i `divisibleBy` 15 | |
then Left "FizzBuzz" | |
else Right i | |
handle5 = if i `divisibleBy` 5 | |
then Left "Fizz" | |
else Right i | |
handle3 = if i `divisibleBy` 3 | |
then Left "Buzz" | |
else Right i | |
notDivisible = Left (show i) | |
divisibleBy numerator denominator = numerator `mod` denominator == 0 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment