Skip to content

Instantly share code, notes, and snippets.

@StevenXL
Created December 19, 2018 22:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save StevenXL/b6a5ebfd1e2198b2f5351bbdf3c5ace9 to your computer and use it in GitHub Desktop.
Save StevenXL/b6a5ebfd1e2198b2f5351bbdf3c5ace9 to your computer and use it in GitHub Desktop.
On over-engineered example of FizzBuzz in Haskell
-- 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