Is there a more succinct way to write code like this that has a lot of sharing between branches of the case statement?
(This is the main part of my solution to one of the Round 1 problems of the Facebook Hacker Cup 2012. The round is over.)
type Memoizable a = a -> a
recoveries :: Int -> Memoizable (String -> Int)
recoveries m = go
where go self ('0':_) = 0
go self [] = 1
go self ss = flip rem 0xfaceb00c $ case analysis ss of
HasMany
| cValid -> cCase + bCase + aCase
| bValid -> bCase + aCase
| aValid -> aCase
Has2
| bValid -> bCase + aCase
| aValid -> aCase
Has1
| aValid -> aCase
_
| otherwise -> 0
where a = digitToInt (first ss)
b = a * 10 + digitToInt (second ss)
c = b * 10 + digitToInt (third ss)
aValid = a <= m
bValid = b <= m
cValid = c <= m
aCase = self (drop 1 ss)
bCase = self (drop 2 ss)
cCase = self (drop 3 ss)
-- not to be confused with functions of same name from Control.Arrow
first (x:_) = x
second (_:x:_) = x
third (_:_:x:_) = x
data Analysis = Empty | Has1 | Has2 | HasMany
-- a dumb little function to help with safety and sharing
analysis :: [a] -> Analysis
analysis [] = Empty
analysis [_] = Has1
analysis [_,_] = Has2
analysis _ = HasMany