4 ms·
I'd like to understand more that second point. How is Maybe not morally equivalent to a conditional? If my ifs were in a separate function that would also work,
by aristus 14y ago
I'd like to understand more that second point. How is Maybe not morally equivalent to a conditional? If my ifs were in a separate function that would also work, no?
- KirinDave 14y ago> How is Maybe not morally equivalent to a conditional? Maybe is a way to represent conditionals that is amenable to higher order patterns. if-then-else conditionals are simply that, whereas Maybe is amenable to Monad, Monoid, and Applicative functor laws. So when we pull out mconcat or <> or mappend, we're actually talking about a higher order pattern. As I mentioned in the article, we could take that same fizzbuzz code and modify it in many ways under the monoid rules. For example, a different harness in the main function could use Map String Integer and completely change the behavior with the same fizzbuzz function.
- KirinDave 14y agoI had a bit more time to spare and thought I'd give you an example of this. First, let's rewrite fizzbuzz to totally get rid of any mention Maybe; we'll deal with it outside: fizzbuzz d i = mconcat (rules <*> pure i) <|> pure (d i) This is written in terms of an Applicative Functor and a Monoid. And then can just do some haskell things. I've done less golfing here and more type signatures to make the code more approachable. {-# LANGUAGE MonadComprehensions, OverlappingInstances, FlexibleInstances#-} module Main where import Control.Applicative import Data.Monoid import Control.Monad import Data.Maybe import Data.List import qualified Data.HashMap.Strict as M import System.Environment -- Let's make it clear what we're working with. type Counter = M.HashMap String Integer -- We want an instance slightly different from the default. instance Monoid Counter where mempty = M.empty mappend = M.unionWith (+) factors = [(3, "fizz"), (5, "buzz"), (7, "bazz")] -- Our rule function is slightly different. -- Not unexpected, since our logic has changed. But we could generalize -- this further! rules :: [(Integer -> Maybe Counter)] rules = [\i -> [M.singleton res 1 | i `rem` fac == 0] | (fac,res) <- factors] -- Fizzbuzz remains unchanged. fizzbuzz d i = mconcat (rules <*> pure i) <|> pure (d i) main = do upTo <- fmap (maybe 100 read . listToMaybe) getArgs let results = foldl' mappend mempty [ fromJust $ fizzbuzz (const mempty) i | i <- [1..upTo] ] putStrLn $ show results And then a typical session: ~/P/h/fb-toys > time ./fbg3 10000000 fromList [("bazz",1428571),("fizz",3333333),("buzz",2000000)] 3.58 real 3.54 user 0.03 sys Which is a pretty expensive way to avoid doing algebra, but the point is that we're talking about very high level patterns here for fizzbuzz. Fizzbuzz is probably a bad name here, it's more like mergeOptionalPatterns. I'm willing to bet if I dug a round a bit in parser combinator libraries I could find something that does nearly exactly this. I confess I had to play with it a bit to get it to play nice with large inputs.