patternMinor
A guarded FizzBuzz
Viewed 0 times
guardedfizzbuzzstackoverflow
Problem
Vogel612 and I decided to take a shot at the StackSTV challenge in Haskell. This is part of the CRitter Collaboration challenge. Except I don't know any Haskell. So let's try a FizzBuzz first!
I'm quite pleased with the readability of the following code. Let me take a shot at trying to explain how it works.
I'm not too fond of the
A monadic action is required because I'm directly handling the I/O and all I/O is considered "impure" by Haskell. Everything impure should be wrapped in a Monad.
Basically, I iterate over every number in the range of 1 to 100 inclusive and put it in
I think using pattern guards like I did here is idiomatic. It feels extensible, and that's a good thing for future Haskell solutions. Feel free to poke any holes in my code and/or theory.
I'm quite pleased with the readability of the following code. Let me take a shot at trying to explain how it works.
main = mapM_ (putStrLn . fizzbuzzer) [1..100]
fizzbuzzer number | mod number 15 == 0 = "FizzBuzz"
| mod number 3 == 0 = "Fizz"
| mod number 5 == 0 = "Buzz"
| otherwise = show numberI'm not too fond of the
mod number 15 part in there, but I'll explain why I think it can't be done without.mapM_: Map each element of a structure to a monadic action, evaluate these actions from left to right, and ignore the results[1]. mapM would work here as well, except we don't care about the output anyway. Right?A monadic action is required because I'm directly handling the I/O and all I/O is considered "impure" by Haskell. Everything impure should be wrapped in a Monad.
Basically, I iterate over every number in the range of 1 to 100 inclusive and put it in
fizzbuzzer. Depending on whether the number is a multiple of 3, 5, 15 or none of those, a String is selected. This get's pushed into putStrLn which outputs the String. Because only one response can be selected, the output for being divisible by 15 has to be explicitly mentioned.I think using pattern guards like I did here is idiomatic. It feels extensible, and that's a good thing for future Haskell solutions. Feel free to poke any holes in my code and/or theory.
Solution
First things first: is this a good variant of FizzBuzz? Well, yes. I would probably write the same variant, except for whitespace and types.
In your usual Haskell code, you want to annotate the top-level bindings with their types:
GHC will usually infer the type correctly, but it's better to add type annotations to all top-level bindings.
Now to your remarks:
Well, if you change
A monadic action is required because I'm directly handling the I/O and all I/O is considered "impure" by Haskell.
Hm. At some point, you have to use
where we have only one
Everything impure should be wrapped in a Monad.
Yes and no. Monads in Haskell just provide an abstraction to chain operations together. There are many monads where you can "escape" back to your usual world, e.g. the
The important part about
Either way, as I already said, your code is fine (except for missing type signatures). Note that you've only used "guards", though, not pattern guards, since you do not actually use a pattern in your code, just boolean expressions.
(*): well, there is one, but you should only ever use it if you know completely what you're doing
In your usual Haskell code, you want to annotate the top-level bindings with their types:
main :: IO () -- here
main = mapM_ (putStrLn . fizzbuzzer) [1..100]
fizzbuzzer :: Int -> String -- here
fizzbuzzer number
| number `mod` 15 == 0 = "FizzBuzz" -- whitespace is personal
| number `mod` 3 == 0 = "Fizz" -- preference, as is
| number `mod` 5 == 0 = "Buzz" -- infix style
| otherwise = show numberGHC will usually infer the type correctly, but it's better to add type annotations to all top-level bindings.
Now to your remarks:
mapM_: … mapM would work here as well, except we don't care about the output anyway. Right?Well, if you change
main's type to IO (), it will not work anymore.A monadic action is required because I'm directly handling the I/O and all I/O is considered "impure" by Haskell.
Hm. At some point, you have to use
IO, at least in main. However, we could also writemain = putStr (unlines (map fizzbuzzer ([1..100])))where we have only one
IO. Either way, I'm not 100% happy with your second sentence:Everything impure should be wrapped in a Monad.
Yes and no. Monads in Haskell just provide an abstraction to chain operations together. There are many monads where you can "escape" back to your usual world, e.g. the
Identity monad:newtype Identity a = Identity { runIdentity :: a }
instance Functor Identity where
fmap f = Identity . f . runIdentity
instance Applicative Identity where
pure = Identity
(Identity f) (Identity x) = Identity (f x)
instance Monad Identity where
x >>= f = f (runIdentity x)The important part about
IO however is, that there is no IO a -> a function(*). That's what keeps pure and impure code apart.Either way, as I already said, your code is fine (except for missing type signatures). Note that you've only used "guards", though, not pattern guards, since you do not actually use a pattern in your code, just boolean expressions.
(*): well, there is one, but you should only ever use it if you know completely what you're doing
Code Snippets
main :: IO () -- here
main = mapM_ (putStrLn . fizzbuzzer) [1..100]
fizzbuzzer :: Int -> String -- here
fizzbuzzer number
| number `mod` 15 == 0 = "FizzBuzz" -- whitespace is personal
| number `mod` 3 == 0 = "Fizz" -- preference, as is
| number `mod` 5 == 0 = "Buzz" -- infix style
| otherwise = show numbermain = putStr (unlines (map fizzbuzzer ([1..100])))newtype Identity a = Identity { runIdentity :: a }
instance Functor Identity where
fmap f = Identity . f . runIdentity
instance Applicative Identity where
pure = Identity
(Identity f) <*> (Identity x) = Identity (f x)
instance Monad Identity where
x >>= f = f (runIdentity x)Context
StackExchange Code Review Q#140294, answer score: 4
Revisions (0)
No revisions yet.