This question here is related to
Haskell Input Return Tuple
I wonder how we can passes the input from monad IO to another function in order to do some computation.
Actually what i want is something like
-- First Example
test = savefile investinput
-- Second Example
maxinvest :: a
maxinvest = liftM maximuminvest maxinvestinput
maxinvestinput :: IO()
maxinvestinput = do
str <- readFile "C:\\Invest.txt"
let cont = words str
let mytuple = converttuple cont
let myint = getint mytuple
putStrLn ""
-- Convert to Tuple
converttuple :: [String] -> [(String, Integer)]
converttuple [] = []
converttuple (x:y:z) = (x, read y):converttuple z
-- Get Integer
getint :: [(String, Integer)] -> [Integer]
getint [] = []
getint (x:xs) = snd (x) : getint xs
-- Search Maximum Invest
maximuminvest :: (Ord a) => [a] -> a
maximuminvest [] = error "Empty Invest Amount List"
maximuminvest [x] = x
maximuminvest (x:xs)
| x > maxTail = x
| otherwise = maxTail
where maxTail = maximuminvest xs
In the second example, the maxinvestinput is read from file and convert the data to the type maximuminvest expected.
Please help.
Thanks.
First, I think you’re having some basic issues with understanding Haskell, so let’s go through building this step by step. Hopefully you’ll find this helpful. Some of it will just arrive at the code you have, and some of it will not, but it is a slowed-down version of what I’d be thinking about as I wrote this code. After that, I’ll try to answer your one particular question.
I’m not quite sure what you want your program to do. I understand that you want a program which reads as input a file containing a list of people and their investments. However, I’m not sure what you want to do with it. You seem to (a) want a sensible data structure (
[(String,Integer)]), but then (b) only use the integers, so I’ll suppose that you want to do something with the strings too. Let’s go through this. First, you want a function that can, given a list of integers, return the maximum. You call thismaximuminvest, but this function is more general that just investments, so why not call itmaximum? As it turns out, this function already exists. How could you know this? I recommend Hoogle—it’s a Haskell search engine which lets you search both function names and types. You want a function from lists of integers to a single integer, so let’s search for that. As it turns out, the first result ismaximum, which is the more general version of what you want. But for learning purposes, let’s suppose you want to write it yourself; in that case, your implementation is just fine.Alright, now we can compute the maximum. But first, we need to construct our list. We’re going to need a function of type
[String] -> [(String,Integer)]to convert our formattingless list into a sensible one. Well, to get an integer from a string, we’ll need to useread. Long story short, your current implementation of this is also fine, though I would (a) add anerrorcase for the one-item list (or, if I were feeling nice, just have it return an empty list to ignore the final item of odd-length lists), and (b) use a name with a capital letter, so I could tell the words apart (and probably a different name):Now that we have these, we can provide ourselves with a convenience function,
maxInvestment :: [String] -> Integer. The only thing missing is the ability to go from the tupled list to a list of integers. There are several ways to solve this. One is the one you have, though that would be unusual in Haskell. A second would be to usemap :: (a -> b) -> [a] -> [b]. This is a function which applies a function to every element of a list. Thus, yourgetintis equivalent to the simplermap snd. The nicest way would probably be to useData.List.maximumBy :: :: (a -> a -> Ordering) -> [a] -> a. This is likemaximum, but it allows you to use a comparison function of your own. And usingData.Ord.comparing :: Ord a => (b -> a) -> b -> b -> Ordering, things become nice. This function allows you to compare two arbitrary objects by converting them to something which can be compared. Thus, I would writeThough you could also write
maxInvestment = maximum . map snd . tupledInvestors.Alright, now on to the IO. Your main function, then, wants to read from a specific file, compute the maximum investment, and print that out. One way to represent that is as a series of three distinct steps:
(The
$operator, if you haven’t seen it, is just function application, but with more convenient precedence; it has type(a -> b) -> a -> b, which should make sense.) But thatlet maxInvseems pretty pointless, so we can get rid of that:The
., if you haven’t seen it yet, is function composition;f . gis the same as\x -> f (g x). (It has type(b -> c) -> (a -> b) -> a -> c, which should, with some thought, make sense.) Thus,f . g $ h xis the same asf (g (h x)), only easier to read.Now, we were able to get rid of the
let. What about the<-? For that, we can use the=<< :: Monad m => (a -> m b) -> m a -> m boperator. Note that it’s almost like$, but with anmtainting almost everything. This allows us to take a monadic value (here, thereadFile "C:\\Invest.txt" :: IO String), pass it to a function which turns a plain value into a monadic value, and get that monadic value. Thus, we haveThat should be clear, I hope, especially if you think of
=<<as a monadic$.I’m not sure what’s happening with
testfile; if you edit your question to reflect that, I’ll try to update my answer.One more thing. You said
As with everything in Haskell, this is a question of types. So let’s puzzle through the types here. You have some function
f :: a -> band some monadic valuem :: IO a. You want to usefto get a value of typeb. This is impossible, as I explained in my answer to your other question; however, you can get something of typeIO b. Thus, you need a function which takes yourfand gives you a monadic version. In other words, something with typeMonad m => (a -> b) -> (m a -> m b). If we plug that into Hoogle, the first result isControl.Monad.liftM, which has precisely that type signature. Thus, you can treatliftMas a slightly different “monadic$” than=<<:f `liftM` mappliesfto the pure result ofm(in accordance with whichever monad you’re using) and returns the monadic result. The difference is thatliftMtakes a pure function on the left, and=<<takes a partially-monadic one.Another way to write the same thing is with
do-notation:This says “get the
xout ofm, applyfto it, and lift the result back into the monad.” This is the same as the statementreturn . f =<< m, which is preciselyliftMagain. Firstfperforms a pure computation; its result is passed intoreturn(via.), which lifts the pure value into the monad; and then this partially-monadic function is applied, via=<,, tom.It’s late, so I’m not sure how much sense that made. Let me try to sum it up. In short, there is no general way to leave a monad. When you want to perform computation on monadic values, you lift pure values (including functions) into the monad, and not the other way around; that could violate purity, which would be Very Bad™.
I hope that actually answered your question. Let me know if it didn’t, so I can try to make it more helpful!