Feb 26 2008

My shame is complete

Published by Dougal at 11:54 pm under Humour, Maths & Computer Science, Programming

And Reg will not be happy.

module Main where
 
import Data.Maybe
 
fizz = concat $ repeat $ replicate 2 Nothing ++ [Just "Fizz"]
buzz = concat $ repeat $ replicate 4 Nothing ++ [Just "Buzz"]
fizzbuzz = zipWith (maybeWith (++)) fizz buzz
numbers = map (Just . show) [1..]
 
maybeWith f (Just a) (Just b) = Just (a `f` b) 
maybeWith _ (Just a) Nothing  = Just a
maybeWith _  Nothing (Just b) = Just b
maybeWith _  Nothing Nothing  = Nothing
 
-- an infinite list of fizzbuzzes
results = map fromJust $ zipWith (maybeWith const) fizzbuzz numbers

I bothered posting this at all because I feel sure that maybeWith can be implemented as a simple combination of other functions. It’s sort of like liftM2, except it doesn’t throw away good values when it has one good and one bad.

Also, I used it twice in five lines, so it surely must be a known function. But for the life of me I can’t think what it could be.

10 Responses to “My shame is complete”

  1. Robert Hulmeon 27 Feb 2008 at 1:01 am

    What the hell? There’s no way I’m accepting this.

    I want a solution with Monad Transformers and Arrows.

    What are they teaching you kids these days?

  2. Greg Buchholzon 27 Feb 2008 at 1:02 am

    What about (untested):

    maybeWith f a b = liftM2 f a b `mplus` a `mplus` b
  3. Greg Buchholzon 27 Feb 2008 at 1:04 am

    …there’s supposed to be backquotes around the “mplus”. Might be nice to have a preview feature.

  4. Chris Kon 27 Feb 2008 at 2:52 am

    Well, Robert, you could look at the “main1” version at http://hpaste.org/5934 to see “ContT IO” used. And using a new Monoid in main2 and main3 is one of my favorite styles. It combines the separate Fizz and Buzz specifications to solve the problem. So adding new rules that combine in the same way is trivial. And they all should scale well to large numbers.

  5. Dougalon 27 Feb 2008 at 9:22 am

    @Greg:

    Yep, you’re right. That works perfectly. (I fixed the formatting for you. The comments use Markdown syntax for future reference.)

    @Chris:

    That looks pretty interesting. I still don’t have a proper understanding of continuation use but I can see the gist of your solution.

  6. Robert Hulmeon 27 Feb 2008 at 11:08 am

    You made programming.reddit!

  7. Dougalon 27 Feb 2008 at 11:36 am

    Yeah Rob, it’s a wee bit embarrassing considering the less-than-intellectually-heavyweight subject of this post. ;-)

  8. liz Hareon 27 Feb 2008 at 10:27 pm

    I wish I understood these replies, but I can tell tell it’s exciting stuff

  9. apfelmuson 28 Feb 2008 at 2:12 am

    Seeing Maybe a as the a finite map (container) with only one possible key (think of Data.Map () a), the canonical name for maybeWith would be unionWith. This doesn’t conceptually fit into Monad or MonadPlus, so I don’t know a really short way to get it from there.

    However, the cases maybeWith (++) and maybeWith const are special. The latter is the monoid operation on Maybe [a] and the latter is mplus. (It can be debated whether the Monoid instance is wrong in the sense that we should have mappend = mplus.) In other words, we have

    maybeWith (++) = mappend maybeWith const = mplus

    And to avoid the potentially “unsafe” call to fromJust (could raise an exception), we can appeal to

    x mplus Just y = fromMaybe y x

    Putting everything together, we get the solution

    ...
    fizzbuzz = zipWith mappend fizz buzz
    numbers = map show [1..]
    results = zipWith fromMaybe numbers fizzbuzz

  10. Porgeson 12 Mar 2008 at 3:32 am

    I had a similar version which I posted shortly after I saw this… it doesn’t seem to have shown up in the trackbacks for this post, so I’ll link it manually :)

    My version.

Trackback URI | Comments RSS

Leave a Reply