This is how I write your code:
increasing :: Integer -> [Integer] increasing 1 = [1..9] increasing n = let allEndings x = map (10*x +) [x `mod` 10 .. 9] in concatMap allEndings $ increasing (n - 1)
I came to this code as follows. The first thing I did was use pattern matching instead of guards, as it is clearer here. The next thing I did was remove liftM2 s. They are not needed here because they are always called with one size-one list; in this case, this is the same as calling map . So, liftM2 (*) ps [10] is just map (* 10) ps and similarly for other call sites. If you need a general replacement for liftM2 , you can use Control.Applicative <$> (it's just fmap ) and <*> to replace liftMn for any n : liftMn fabc ... z becomes f <$> a <*> b <*> c <*> ... <*> z . Whether it is more enjoyable is a matter of taste; I like it so. 1 But here we can completely rule it out.
The next place I simplified the source code is do ... You never use the fact that you are in a do block, and therefore the code may become
let ps = increasing (n - 1) last = map (`mod` 10) ps next = map (* 10) ps in alternateEndings next last
From here, having come to my code, it was essentially connected with a record holding all your map together. One of the remaining calls that was not map was zipWith . But since you have zipWith alts next last , you only work with 10*p and p `mod` 10 at a time, so we can calculate them in the same function. This leads to
let ps = increasing (n - 1) in concat $ map alts ps where alts p = map (10*p +) [y `mod` 10..9]
And this is basically my code: concat $ map ... should always become concatMap (which, by the way, is =<< in the list monad), we use only ps so that we can collapse it, and I'm from let to where .
1: Technically, this only works for Applicative s, so if you use a monad that has not been done, <$> is `liftM` and <*> `ap` . All monads can be made by applicative functors, and many of them were.
Antal spector-zabusky
source share