How do I get the parameters out of the Maybe wrapper correctly? - haskell

I want to get the Maybe parameters out of the Maybe wrapper
A wrapper that contains one or more approximation results
data CalculatedPoints =
Linear {xPoints :: [Maybe Double], yPoints :: [Maybe Double] }
| Segment {xPoints :: [Maybe Double], yPoints :: [Maybe Double]}
deriving Show
Trying to get values
main = do
resultPoints <- parseInput
case resultPoints of
[Nothing, Nothing] -> putStrLn "Calculation failed. Perhaps you did not specify a method."
[lpoints, Nothing] -> do
putStrLn "Linear Aproxiation"
let (xl, yl) = fromMaybe (Nothing, Nothing) lpoints -- THIS
-- prettyPoints xl yl
print $ xl
[Nothing, spoints] -> do
putStrLn "Segment Aproxiation"
print spoints
[lpoints, spoints] -> do
putStrLn "Linear Aproxiation"
print lpoints
putStrLn "Segment Aproxiation"
print spoints
I get this error
warning: [-Wdeferred-type-errors]
    • Couldn't match type ‘CalculatedPoints’ with ‘(Maybe a, Maybe a1)
      Expected: Maybe (Maybe a, Maybe a1)         Actual: Maybe CalculatedPoints     • In the second argument of ‘fromMaybe’, namely ‘lpoints’
      In the expression: fromMaybe (Nothing, Nothing) lpoints       In a pattern binding:
        (xl, yl) = fromMaybe (Nothing, Nothing) lpoint
P.S
I decided that parseInput was important for the context, so it turns out that calculatePoints is also necessary, so I included them
calculatePoints :: Interval -> Double -> Points -> Bool -> Bool -> [Maybe CalculatedPoints]
calculatePoints interval step points True True =
[ Just (linearApproximation interval step points),
Just (segmentApproximation interval step points)
]
calculatePoints interval step points True False = [Just (linearApproximation interval step points), Nothing]
calculatePoints interval step points False True = [Nothing, Just (segmentApproximation interval step points)]
calculatePoints _ _ _ False False = [Nothing, Nothing]
parseInput :: IO [Maybe CalculatedPoints]
parseInput = do
input <- cmdArgs inputOptions
points <- case file input of
"" -> getPointsNoFile []
path -> getPointsFile path
return $ calculatePoints (left input, right input) 0.95 points (lm input) (sm input)

Notice that fromMaybe needs a default value of the same type. Below, your code commented with the mistake
main = do
resultPoints <- parseInput -- This has type [Maybe CalculatedPoints]
case resultPoints of
-- This is the case where input is a list with two Nothing's . Looks good :)
[Nothing, Nothing] -> putStrLn "Calculation failed. Perhaps you did not specify a method."
-- This case is missleading. First, You can match directly (Just calcpoints)
-- because the case where lpoints is Nothing, has being match before.
-- so there is no need to have an irrefutable pattern (i.e. a variable ignoring the shape)
-- Nevertheless, the error is below, not here
[lpoints, Nothing] -> do
putStrLn "Linear Aproxiation"
-- |- This has type a -> Maybe a -> a (sumary: you provide a default value for Nothing, otherwise extract whatever is within Just)
-- | |- The default value has type "(Maybe a, Maybe a)" => a tuple of maybes
-- | | |- This has value (Maybe CalculatedPoints)
let (xl, yl) = fromMaybe (Nothing, Nothing) lpoints -- THIS
-- |- Also this has type tuple of maybes. Therefore GHC is complaining
-- "you want me to extract a tuple but the value you provide is Maybe CalculatedPoints
-- How in the heck I get a tuple from that??"
prettyPoints xl yl
print $ xl
.
.
.
Now, If you want to type check your code, It should be something like below. Notice that It isn't a very good / idiomatic haskell code.
main = do
resultPoints <- parseInput
case resultPoints of
[Nothing, Nothing] -> putStrLn "Calculation failed. Perhaps you did not specify a method."
[lpoints, Nothing] -> do
putStrLn "Linear Aproxiation"
-- |- Provide a default value of the same type as the lpoints has "inside" the Just
let points = fromMaybe (Linear [] []) lpoints
-- |- not sure about this... But you can work it out
prettyPoints points??
print $ xl
Now, If you want more idiomatic code I'd suggest
main = do
resultPoints <- parseInput
case resultPoints of
[Nothing, Nothing] -> putStrLn "Calculation failed. Perhaps you did not specify a method."
[Just (Linear xl yl), Nothing] -> do
putStrLn "Linear Aproxiation"
prettyPoints xl yl
print $ xl
.
.
.

I think I'd just delete most of this code. Here's a much simpler main with the same behavior:
main = do
input <- cmdArgs inputOptions
points <- case file input of
"" -> getPointsNoFile []
path -> getPointsFile path
let interval = (left input, right input)
unless (lm input || sm input) (putStrLn "Calculation failed. Perhaps you did not specify a method.")
when (lm input) $ do
putStrLn "Linear Aproxiation" -- sic
print (linearApproximation interval 0.95 points)
when (sm input) $ do
putStrLn "Segment Aproxiation"
print (segmentApproximation interval 0.95 points)
No Maybe needed; no CalculatedPoints needed (...probably. unless linearApproximation and segmentApproximation are weirder than their names sound); no fragile guaranteed-length-two list needed; and less code repetition between cases. There is still a little bit of repetition between the linear and segment printing code; if you really want, you could abstract those a little bit.
data ApproximationMethod = AM
{ name :: String
, approx :: Interval -> Double -> Points -> Points
, active :: InputOptions -> Bool
}
allMethods :: [ApproximationMethod]
allMethods = [AM "Linear" linearApproximation lm, AM "Segment" segmentApproximation sm]
main = do
{- ... -}
for_ allMethods $ \am -> when (active am input) $ do
putStrLn $ name am ++ " Aproxiation"
print (approx am interval 0.95 points)
But for this little repeated code, that seems like overkill.

Related

What is the difference between `readMay` and `readMaybe`?

The two functions readMay and readMaybe have the same signature Read a => String -> Maybe a.
Is there any difference between them? If so, what are they? Which of the two function should be preferred?
There is no difference. Here's how readMay's defined:
-- | This function provides a more precise error message than 'readEither' from 'base'.
readEitherSafe :: Read a => String -> Either String a
readEitherSafe s = case [x | (x,t) <- reads s, ("","") <- lex t] of
[x] -> Right x
[] -> Left $ "no parse on " ++ prefix
_ -> Left $ "ambiguous parse on " ++ prefix
where
maxLength = 15
prefix = '\"' : a ++ if length s <= maxLength then b ++ "\"" else "...\""
where (a,b) = splitAt (maxLength - 3) s
readMay :: Read a => String -> Maybe a
readMay = eitherToMaybe . readEitherSafe
And here is readMaybe:
-- | Parse a string using the 'Read' instance.
-- Succeeds if there is exactly one valid result.
-- A 'Left' value indicates a parse error.
--
-- #since 4.6.0.0
readEither :: Read a => String -> Either String a
readEither s =
case [ x | (x,"") <- readPrec_to_S read' minPrec s ] of
[x] -> Right x
[] -> Left "Prelude.read: no parse"
_ -> Left "Prelude.read: ambiguous parse"
where
read' =
do x <- readPrec
lift P.skipSpaces
return x
-- | Parse a string using the 'Read' instance.
-- Succeeds if there is exactly one valid result.
--
-- #since 4.6.0.0
readMaybe :: Read a => String -> Maybe a
readMaybe s = case readEither s of
Left _ -> Nothing
Right a -> Just a
They differ in the intermediate error message (readEitherSafe shows the input), but the result will be same.
readMay from Safe predates readMaybe from Text.Read. Unless you're on a base version less than 4.6.0.0, use readMaybe from Text.Read as it does not need another package.

Haskell Processing text from a file

Hi Guys,
1. What do I want to do?
I get a 1-lined file with text
"Bangabang [Just 3, Nothing, Just 1, Nothing] [Nothing, Nothing, Nothing, Nothing] [Nothing, Nothing, Just 4, Nothing] [Nothing, Just 3, Nothing, Nothing]"
I want to read this text from a file and convert it to:
[[Just 3, Nothing, Just 1, Nothing], [Nothing, Nothing, Nothing, Nothing], [Nothing, Nothing, Just 4, Nothing], [Nothing, Just 3, Nothing, Nothing]]
Which is a [[Maybe Integer]] type.
2. What have I already done?
I can modify normal String to Maybe Integer
My String:
xxx = "Bangabang [Just 3, Nothing, Just 1, Nothing] [Nothing, Nothing, Nothing, Nothing] [Nothing, Nothing, Just 4, Nothing] [Nothing, Just 3, Nothing, Nothing]"
after executing stripChars ",]" $ drop 10 xxx I get:
"Just 31 Nothing Just 1 Nothing [Nothing Nothing Nothing Nothing [Nothing Nothing Just 4 Nothing [Nothing Just 3 Nothing Nothing"
after next commands map (splitOn " ") $ splitOn "[" I have:
[["Just","31","Nothing","Just","1","Nothing",""],["Nothing","Nothing","Nothing","Nothing",""],["Nothing","Nothing","Just","4","Nothing",""],["Nothing","Just","3","Nothing","Nothing"]]
Now I have to cut off that empty strings "" using cleany
And finally change [[String]] to [[Maybe Integer]] using cuty
[[Just 31,Nothing,Just 1,Nothing],[Nothing,Nothing,Nothing,Nothing],[Nothing,Nothing,Just 4,Nothing],[Nothing,Just 3,Nothing,Nothing]]
That is what I wanted to have!
3. The problem is...
...how can I execute this method:
parse xxx = cuty $ cleany $ map (splitOn " ") $ splitOn "[" $ stripChars ",]" $ drop 10 xxx
on text read from file (which is IO String type)?
This is my first Haskell project, so my functions may reinvent the wheel or do worse things :/
Used functions:
main do
text <- readFile "test.txt"
let l = lines
map parse . l
-- deletes unwanted characters from a String
stripChars :: String -> String -> String
stripChars = filter . flip notElem
-- converts String to Maybe a
maybeRead :: Read a => String -> Maybe a
maybeRead s = case reads s of
[(x,"")] -> Just x
_ -> Nothing
-- convert(with subfunction conv, because I don't know how to make it one function)
conv:: [String] -> [Maybe Integer]
conv[] = []
conv(x:xs) = if x == "Just" then conv xs
else maybeRead x: conv xs
convert:: [[String]] -> [[Maybe Integer]]
convert[] = []
convert(x:xs) = conv x : convert xs
-- cleany (with subfunction clean, because I don't know how to make it one function)
clean :: [String] -> [String]
clean [] = []
clean (x:xs) = if x == "" then clean xs
else x : clean xs
cleany :: [[String]] -> [[String]]
cleany [] = []
cleany (x:xs) = clean x : cleany xs
I'll assume you're ok with a parser that does zero to minimal error checking. Haskell has great libraries for parsing, and later I'll amend my answer with some alternatives you should look at.
Instead of using splitOn I would recommend writing these functions:
takeList :: String -> (String, String)
-- returns the match text and the text following the match
-- e.g. takeList " [1,2,3] ..." returns ("[1,2,3]", " ...")
takeLists :: String -> [String]
-- parses a sequence of lists separated by spaces
-- into a list of matches
I'll leave takeList as an exercise. I like to use span and break from Data.List for these kinds of simple parsers.
In terms of takeList, here is how you might write takeLists:
takeLists :: String -> [ String ]
takeLists str =
let s1 = dropWhile (/= '[') str
in if null s1
then []
else let (s2,s3) = takeList s1
in s2 : takeLists s3
For example, takeLists " [123] [4,5,6] [7,8] " will return:
[ "[123]", "[4,5,6]", "[7,8]" ]
Finally, to convert each string in this list to Haskell values, just use read.
answer :: [ [Int] ]
answer = map read (takeLists " [123] [4,5,6] [7,8] ")
Update
Using the ReadP and ReadS parsers available in the base libraries:
import Text.ParserCombinators.ReadP
bang :: ReadP [[Maybe Int]]
bang = do string "Bangabang"
skipSpaces
xs <- sepBy1 (readS_to_P reads) skipSpaces
eof
return xs
input = "Bangabang [Just 3, Nothing, Just 1, Nothing] [Nothing, Nothing, Nothing, Nothing] [Nothing, Nothing, Just 4, Nothing] [Nothing, Just 3, Nothing, Nothing]"
runParser p input = case (readP_to_S p) input of
[] -> error "no parses"
((a,_):_) -> print a
example = runParser bang input
You can use directly Read instance.
data Bangabang = Bangabang [Maybe Integer]
[Maybe Integer]
[Maybe Integer]
[Maybe Integer] deriving (Read, Show)
now, you can use all Read machinery (read, reads, readIO, ...), inferred from types. E.g.
readBangabang :: String -> Bangabang
readBangabang = read
If data came from file
readFile "foo.txt" >>= print . readBangabang

Non-exhaustive patterns in lambda

I am getting Non-exhaustive patterns in lambda. I am not sure of the cause yet. Please anyone how to fix it. The code is below:
import Control.Monad
import Data.List
time_spent h1 h2 = max (abs (fst h1 - fst h2)) (abs (snd h1 - snd h2))
meeting_point xs = foldl' (find_min_time) maxBound xs
where
time_to_point p = foldl' (\tacc p' -> tacc + (time_spent p p')) 0 xs
find_min_time min_time p = let x = time_to_point p in if x < min_time then x else min_time
main = do
n <- readLn :: IO Int
points <- fmap (map (\[x,y] -> (x,y)) . map (map (read :: String->Int)) . map words . lines) getContents
putStrLn $ show $ meeting_point points
This is the lambda with the non-exhaustive patterns: \[x,y] -> (x,y).
The non-exhaustive pattern is because the argument you've specified, [x,y] doesn't match any possible list - it only matches lists with precisely two elements.
I would suggest replacing it with a separate function with an error case to print out the unexpected data in an error message so you can debug further, e.g.:
f [x,y] = (x, y)
f l = error $ "Unexpected list: " ++ show l
...
points <- fmap (map f . map ...)
As an addition to #GaneshSittampalam's answer, you could also do this with more graceful error handling using the Maybe monad, the mapM function from Control.Monad, and readMaybe from Text.Read. I would also recommend refactoring your code so that the parsing is its own function, it makes your main function much cleaner and easier to debug.
import Control.Monad (mapM)
import Text.Read (readMaybe)
toPoint :: [a] -> Maybe (a, a)
toPoint [x, y] = Just (x, y)
toPoint _ = Nothing
This is just a simple pattern matching function that returns Nothing if it gets a list with length not 2. Otherwise it turns it into a 2-tuple and wraps it in Just.
parseData :: String -> Maybe [(Int, Int)]
parseData text = do
-- returns Nothing if a non-Int is encountered
values <- mapM (mapM readMaybe . words) . lines $ text
-- returns Nothing if a line doesn't have exactly 2 values
mapM toPoint values
Your parsing can actually be simplified significantly by using mapM and readMaybe. The type of readMaybe is Read a => String -> Maybe a, and in this case since we've specified the type of parseData to return Maybe [(Int, Int)], the compiler can infer that readMaybe should have the local type of String -> Maybe Int. We still use lines and words in the same way, but now since we use mapM the type of the right hand side of the <- is Maybe [[Int]], so the type of values is [[Int]]. What mapM also does for us is if any of those actions fails, the overall computation exits early with Nothing. Then we simply use mapM toPoint to convert values into a list of points, but also with the failure mechanism built in. We actually could use the more general signature of parseData :: Read a => String -> Maybe [(a, a)], but it isn't necessary.
main = do
n <- readLn :: IO Int
points <- fmap parseData getContents
case points of
Just ps -> print $ meeting_point ps
Nothing -> putStrLn "Invalid data!"
Now we just use fmap parseData on getContents, making points have the type Maybe [(Int, Int)]. Finally, we pattern match on points to print out the result of the meeting_point computation or print a helpful message if something went wrong.
If you wanted even better error handling, you could leverage the Either monad in a similar fashion:
toPoint :: [a] -> Either String (a, a)
toPoint [x, y] = Right (x, y)
toPoint _ = Left "Invalid number of points"
readEither :: Read a => String -> Either String a
readEither text = maybe (Left $ "Invalid parse: " ++ text) Right $ readMaybe text
-- default value ^ Wraps output on success ^
-- Same definition with different type signature and `readEither`
parseData :: String -> Either String [(Int, Int)]
parseData text = do
values <- mapM (mapM readEither . words) . lines $ text
mapM toPoint values
main = do
points <- fmap parseData getContents
case points of
Right ps -> print $ meeting_point ps
Left err -> putStrLn $ "Error: " ++ err

How can I parse a string to a function in Haskell?

I want a function that looks something like this
readFunc :: String -> (Float -> Float)
which operates something like this
>(readFunc "sin") (pi/2)
>1.0
>(readFunc "(+2)") 3.0
>5.0
>(readFunc "(\x -> if x > 5.0 then 5.0 else x)") 2.0
>2.0
>(readFunc "(\x -> if x > 5.0 then 5.0 else x)") 7.0
>5.0
The incredibly naive approach (note this must be compiled with {-# LANGUAGE FlexibleContexts #-})
readFunc :: (Read (Float -> Float)) => String -> (Float -> Float)
readFunc s = read s
gives
No instance for (Read (Float -> Float)) ...
Which makes sense since no such instance exists. I understand that I can parse the input string character by character by writing a map from String to Float -> Float but I want to be able to parse at least the most common functions from prelude, and even that would be way more work than I want to commit to. Is there an easy way of doing this?
Just one solution using hint
import Language.Haskell.Interpreter hiding (typeOf)
import Data.Typeable (typeOf)
data Domain = Dom Float Float Float Float Domain
| SDom Float Float Float Float
deriving (Show, Read)
--gets all the points that will appear in the domain
points (SDom a b c d) m = [(x, y)|x <- [a, a+m .. b], y <- [c, c+m .. d]]
points (Dom a b c d next) m = points next m ++ [(x, y)|x <- [a, a+m .. b], y <- [c, c+m .. d]]
readFunc = do
putStrLn "Enter a domain (as Dom x-min x-max y-min y-max subdomain, or, SDom x-min x-max y-min y-max)"
domain' <- getLine
let domain = (read domain') :: Domain
--
putStrLn "Enter a mesh size"
meshSize' <- getLine
let meshSize = (read meshSize') :: Float
--
putStrLn "Enter an initial value function (as f(x,y))"
func' <- getLine
values' <- runInterpreter $ setImports["Prelude"] >>
eval ("map (\\(x,y) -> " ++ func' ++ ")" ++ show (points domain meshSize))
let values = (\(Right v) -> (read v)::([Float])) values'
--the haskell expression being evaluated
putStrLn $ ("map (\\(x,y) -> " ++ func' ++ ")" ++ show (points domain meshSize))
--prints the actual values
putStrLn $ show values
--the type is indeed [float]
putStrLn $ show $ typeOf values
You can use the hint package, or plugins. I'll show you the former (partly because my Windows installation is clearly a little broken in that cabal doesn't share my belief that I have C installed, so cabal install plugins fails).
String -> Function is easy:
import Language.Haskell.Interpreter
getF :: String -> IO (Either InterpreterError (Float -> Float))
getF xs = runInterpreter $ do
setImports ["Prelude"]
interpret xs (as :: Float -> Float)
You may want to add additional modules to the imports list. This tests out as
ghci> getF "sin" >>= \(Right f) -> print $ f (3.1415927/2)
1.0
ghci> getF "(\\x -> if x > 5.0 then 5.0 else x)" >>= \(Right f) -> print $ f 7
5.0
(Notice the escaping of the escape character \.)
Error messages
As you may have noticed, the result is wrapped in the Either data type. Right f is correct output, whereas Left err gives an InterpreterError message, which is quite helpful:
ghci> getF "sinhh" >>= \(Left err) -> print err
WontCompile [GhcError {errMsg = "Not in scope: `sinhh'\nPerhaps you meant `sinh' (imported from Prelude)"}]
Example toy program
Of course, you can use either with your code to deal with this. Let's make a fake example respond. Your real one will contain all the maths of your program.
respond :: (Float -> Float) -> IO ()
respond f = do
-- insert cunning numerical method instead of
let result = f 5
print result
A simple, one-try, unhelpful version of your program could then be
main =
putStrLn "Enter your function please:"
>> getLine
>>= getF
>>= either print respond
Example sessions
ghci> main
Enter your function please:
\x -> x^2 + 4
29.0
ghci> main
Enter your function please:
ln
WontCompile [GhcError {errMsg = "Not in scope: `ln'"}]
It does type checking for you:
ghci> main
Enter your function please:
(:"yo")
WontCompile [GhcError {errMsg = "Couldn't match expected type `GHC.Types.Float'\n with actual type `GHC.Types.Char'"}]

awkward monad transformer stack

Solving a problem from Google Code Jam (2009.1A.A: "Multi-base happiness") I came up with an awkward (code-wise) solution, and I'm interested in how it could be improved.
The problem description, shortly, is: Find the smallest number bigger than 1 for which iteratively calculating the sum of squares of digits reaches 1, for all bases from a given list.
Or description in pseudo-Haskell (code that would solve it if elem could always work for infinite lists):
solution =
head . (`filter` [2..]) .
all ((1 `elem`) . (`iterate` i) . sumSquareOfDigitsInBase)
And my awkward solution:
By awkward I mean it has this kind of code: happy <- lift . lift . lift $ isHappy Set.empty base cur
I memoize results of the isHappy function. Using the State monad for the memoized results Map.
Trying to find the first solution, I did not use head and filter (like the pseudo-haskell above does), because the computation isn't pure (changes state). So I iterated by using StateT with a counter, and a MaybeT to terminate the computation when condition holds.
Already inside a MaybeT (StateT a (State b)), if the condition doesn't hold for one base, there is no need to check the other ones, so I have another MaybeT in the stack for that.
Code:
import Control.Monad.Maybe
import Control.Monad.State
import Data.Maybe
import qualified Data.Map as Map
import qualified Data.Set as Set
type IsHappyMemo = State (Map.Map (Integer, Integer) Bool)
isHappy :: Set.Set Integer -> Integer -> Integer -> IsHappyMemo Bool
isHappy _ _ 1 = return True
isHappy path base num = do
memo <- get
case Map.lookup (base, num) memo of
Just r -> return r
Nothing -> do
r <- calc
when (num < 1000) . modify $ Map.insert (base, num) r
return r
where
calc
| num `Set.member` path = return False
| otherwise = isHappy (Set.insert num path) base nxt
nxt =
sum . map ((^ (2::Int)) . (`mod` base)) .
takeWhile (not . (== 0)) . iterate (`div` base) $ num
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases =
fmap snd .
(`runStateT` 2) .
runMaybeT .
forever $ do
(`when` mzero) . isJust =<<
runMaybeT (mapM_ f bases)
lift $ modify (+ 1)
where
f base = do
cur <- lift . lift $ get
happy <- lift . lift . lift $ isHappy Set.empty base cur
unless happy mzero
solve :: [String] -> String
solve =
concat .
(`evalState` Map.empty) .
mapM f .
zip [1 :: Integer ..]
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s ++ "\n"
main :: IO ()
main =
getContents >>=
putStr . solve . tail . lines
Other contestants using Haskell did have nicer solutions, but solved the problem differently. My question is about small iterative improvements to my code.
Your solution is certainly awkward in its use (and abuse) of monads:
It is usual to build monads piecemeal by stacking several transformers
It is less usual, but still happens sometimes, to stack several states
It is very unusual to stack several Maybe transformers
It is even more unusual to use MaybeT to interrupt a loop
Your code is a bit too pointless :
(`when` mzero) . isJust =<<
runMaybeT (mapM_ f bases)
instead of the easier to read
let isHappy = isJust $ runMaybeT (mapM_ f bases)
when isHappy mzero
Focusing now on function solve1, let us simplify it.
An easy way to do so is to remove the inner MaybeT monad. Instead of a forever loop which breaks when a happy number is found, you can go the other way around and recurse only if the
number is not happy.
Moreover, you don't really need the State monad either, do you ? One can always replace the state with an explicit argument.
Applying these ideas solve1 now looks much better:
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases = go 2 where
go i = do happyBases <- mapM (\b -> isHappy Set.empty b i) bases
if and happyBases
then return i
else go (i+1)
I would be more han happy with that code.
The rest of your solution is fine.
One thing that bothers me is that you throw away the memo cache for every subproblem. Is there a reason for that?
solve :: [String] -> String
solve =
concat .
(`evalState` Map.empty) .
mapM f .
zip [1 :: Integer ..]
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s ++ "\n"
Wouldn't your solution be more efficient if you reused it instead ?
solve :: [String] -> String
solve cases = (`evalState` Map.empty) $ do
solutions <- mapM f (zip [1 :: Integer ..] cases)
return (unlines solutions)
where
f (idx, prob) = do
s <- solve1 . map read . words $ prob
return $ "Case #" ++ show idx ++ ": " ++ show s
The Monad* classes exist to remove the need for repeated lifting. If you change your signatures like this:
type IsHappyMemo = Map.Map (Integer, Integer) Bool
isHappy :: MonadState IsHappyMemo m => Set.Set Integer -> Integer -> Integer -> m Bool
This way you can remove most of the 'lift's. However, the longest sequence of lifts cannot be removed, since it is a State monad inside a StateT, so using the MonadState type class will give you the outer StateT, where you need tot get to the inner State. You could wrap your State monad in a newtype and make a MonadHappy class, similar to the existing monad classes.
ListT (from the List package) does a much nicer job than MaybeT in stopping the calculation when necessary.
solve1 :: [Integer] -> IsHappyMemo Integer
solve1 bases = do
Cons result _ <- runList . filterL cond $ fromList [2..]
return result
where
cond num = andL . mapL (isHappy Set.empty num) $ fromList bases
Some elaboration on how this works:
Had we used a regular list the code would had looked like this:
solve1 bases = do
result:_ <- filterM cond [2..]
return result
where
cond num = fmap and . mapM (isHappy Set.empty num) bases
This calculation happens in a State monad, but if we'd like to get the resulting state, we'd have a problem, because filterM runs the monadic predicate it gets for every element of [2..], an infinite list.
With the monadic list, filterL cond (fromList [2..]) represents a list that we can access one item at a time as a monadic action, so our monadic predicate cond isn't actually executed (and affecting the state) unless we consume the corresponding list items.
Similarly, implementing cond using andL makes us not calculate and update the state if we already got a False result from one of the isHappy Set.empty num calculations.

Resources