Skip to content

Instantly share code, notes, and snippets.

@beala
Last active August 29, 2015 14:15
Show Gist options
  • Save beala/59908630f12e844db655 to your computer and use it in GitHub Desktop.
Save beala/59908630f12e844db655 to your computer and use it in GitHub Desktop.
Free monad for declarative fizz buzz
{-# LANGUAGE DeriveFunctor #-}
import Control.Monad.Free
import Data.List
data FizzBuzz f a = FizzBuzz (Factor f) String a deriving Functor
newtype Factor a = Factor a
-- A fizz buzz action that produces a String if the Integral is a factor.
fizzbuzz :: (Show a, Integral a) => a -> String -> Free (FizzBuzz a) ()
fizzbuzz fa message = liftF (FizzBuzz (Factor fa) message ())
-- Run a FizzBuzz program over a single Integral.
runFizzBuzz :: (Show a, Integral a) => a -> Free (FizzBuzz a) b -> String
runFizzBuzz i prog = runFizzBuzz_ i [] prog
runFizzBuzz_ :: (Show a, Integral a) => a -> [(Factor a, String)] -> Free (FizzBuzz a) b -> String
runFizzBuzz_ num factors (Free (FizzBuzz fa msg n)) =
runFizzBuzz_ num ((fa, msg):factors) n
runFizzBuzz_ num factors (Pure _) =
let inOrderFactors = reverse factors
divisibleFactors = filter (\ (Factor fa, _) -> num `divisibleBy` fa) inOrderFactors
messages = fmap (\(_, msg) -> msg) divisibleFactors in
if (length messages > 0) then concat messages else show num
where
n `divisibleBy` m = (n `mod` m) == 0
-- Run a FizzBuzz program over a range of Integrals.
runFizzBuzzRange :: (Show a, Integral a) => a -> a -> Free (FizzBuzz a) b -> String
runFizzBuzzRange start end prog =
let messages = fmap (\i -> runFizzBuzz i prog) [start..end] in
intercalate "\n" messages
program :: Free (FizzBuzz Int) ()
program = do
fizzbuzz 5 "Fizz"
fizzbuzz 3 "Buzz"
fizzbuzz 2 "Zork"
fizzbuzz 7 "Bonk"
fizzbuzz 10 "Bang"
main :: IO ()
main = do
putStrLn $ runFizzBuzzRange 1 100 program
@beala
Copy link
Author

beala commented Feb 12, 2015

1
Zork
Buzz
Zork
Fizz
BuzzZork
Bonk
Zork
Buzz
FizzZorkBang
11
BuzzZork
13
ZorkBonk
FizzBuzz
Zork
17
BuzzZork
19
FizzZorkBang
BuzzBonk
Zork
23
BuzzZork
Fizz
Zork
Buzz
ZorkBonk
29
FizzBuzzZorkBang
31
Zork
Buzz
Zork
FizzBonk
BuzzZork
37
Zork
Buzz
FizzZorkBang
41
BuzzZorkBonk
43
Zork
FizzBuzz
Zork
47
BuzzZork
Bonk
FizzZorkBang
Buzz
Zork
53
BuzzZork
Fizz
ZorkBonk
Buzz
Zork
59
FizzBuzzZorkBang
61
Zork
BuzzBonk
Zork
Fizz
BuzzZork
67
Zork
Buzz
FizzZorkBonkBang
71
BuzzZork
73
Zork
FizzBuzz
Zork
Bonk
BuzzZork
79
FizzZorkBang
Buzz
Zork
83
BuzzZorkBonk
Fizz
Zork
Buzz
Zork
89
FizzBuzzZorkBang
Bonk
Zork
Buzz
Zork
Fizz
BuzzZork
97
ZorkBonk
Buzz
FizzZorkBang

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment