Understanding when to use let and <- - haskell

I have been playing around with Haskell for a bit now but I have not fully grasped how to use third party functions that run inside a Monad. Every time I go back to reading articles about Monads, etc. I get a good understanding but when it comes to applying them to real-world code, I cannot figure why a piece of code does not work. I resort to trial and error and usually get it to compile but I feel I should be able to use them properly the first time without trying to go through my heuristic of changes (try let, <-, liftM, etc.)
So I would like to ask a few questions based on this simple function, which admittedly does a lot of interesting things.
import Text.XML.HXT.Core
import Text.HandsomeSoup
import Data.String.Utils
function h = do
let url = myUrlBuilder h
doc = fromUrl url
res = runX $ doc >>> css "strong" /> getText
--nres = liftM rmSpaceAndBang (res)
res
rmSpaceAndBang ps = map (\x-> replace "!" "" (strip x)) ps
The above code compiles. I have purposefully left out the type declarations as what I thought it should be doesn't compile. So here are my questions.
Why can I not do res <- runX ... and return res that way?
Why should res be inside a let statement and not be bound the result of action? As I understand it, do x <- a1; a2 is equivalent to a1 >>= \x -> a2. How is that different when you let x = a1?
When I used <- I got the following error and if not for my trial and error approach I would not have been able to figure out that I need to use let here.
Couldn't match type `[]' with `IO'
Expected type: IO String
Actual type: [String]
While I focused on res above, my lack of understanding applies to other let statements in the function as well.
How do I find the return type of res?
I couldn't figure out a way to search hackage for getText (hxt seems too big to look through module by module. Probably will try Google site search next time). In the end, I ended up typing up some parts of the code in GHCi and did :t res. It told me it is [String]. Is there a better way to do this?
Since res is of type [String] I thought I will put [String] as the return type for my function. But GHC says it should be IO [String] (compiles). Why did :t give me the wrong information first?
When functions return IO String, what's the best way to use pure functions on them?
Now that I am stuck inside IO [String] I need to use to lift everywhere I do string operations. Is there a better way to do this?
Hopefully I will learn enough from this that I will be able to use right syntax without resorting to blindly trying a few combinations.
Update:
The key piece I was missing was the fact res is not a value but rather an action. So I have 2 choices: one is is my above code with let res = but call it at the end and the other is to do res <- but then do return (res).
The advantage of using res <- is that I can get rid of the liftM as res is now [String] (see #duplode's answer below).
Thanks!

In your code, res is an IO [String]. I do not doubt that you got [String] through GHCi at first, but I believe you tested it with
>>> res <- runX $ doc >>> css "strong" /> getText
>>> :t res
res :: [String]
Which is not equivalent to your code. The difference is that let just binds your IO [String] action without running it, while <- in a do block runs the action and binds the result, in this case a [String].
Now that I am stuck inside IO [String] I need to use to lift
everywhere I do string operations. Is there a better way to do this?
Within a do block, sometimes it is more convenient to write:
res <- runX $ doc >>> css "strong" /> getText
return $ rmSpaceAndBang res
Which is strictly equivalent to using liftM (or fmap):
liftM rmSpaceAndBang $ doc >>> css "strong" /> getText

For a fast answer, let doesn't run anything, it's just makes the lhs as a synonym for rhs.
You actually need a monadic function inside the do for computation be executed.
main = do
let func = print "I need to be called"
print "I don't need to be called"
func
outputs:
"I don't need to be called"
"I need to be called"
So res in your code is not a value, it's a monadic action/function.
Remember that <- is tied to >>=, and requires a a -> m b on the rhs.
let has no requirements.

Related

Is there a way to "unwrap" IO monad in Haskell inside another io action?

In particular I want to use pure Haskell functions on the results on console input. I'm curious if exists something like ? operator from rust.
this snippet complains that words expect the type String, while its supplied wrapped into an io action. But i can't even use >>= operator since as far as i understand i cannot instantiate IO constructor directly. So, does it mean all the standard library functions can't work with io even in the scope of io action?
thing :: IO ()
thing = do
let mbwords = words $ getLine ;
Since Haskell is not designed to be totally useless, there must be a way. And there is more than one indeed.
What you looking for is something like this
main = do
line <- getLine
let mbwords = words line
or perhaps
main = do
mbwords <- fmap words getLine
Note you use <- to "open" the monadic value inside the do-block, not let.
Also note instantiating IO constructors is not necessary as long as you have functions that return IO actions, such as print. You can express the same idea with
main = fmap words getLine >>= print
(of course you can use an arbitrary action of the right type instead of print).

Defining a function in haskell

Why does my function give me an out of scope error?
tarefa1 :: [String] -> [String]
tarefa1 linhas = if res == ok then ["OK"] else [show res]
where
(tab,coords) = parteMapa conteudo
erro1 = validaTabuleiro 1 tab
erro2 = validaCoords (length tab + 1) tab coords
res = juntaErro erro1 erro2
The error:
Not in scope: `conteudo'.
conteudo is supposed to be a .txt document that I have in a different file, but I don't know how to make it to load it in this function.
This is not really a good question, since this should be covered by basic Haskell knowledge, and it's clearly homework for those of us who can speak Portuguese. You shouldn't be afraid to ask your teacher for some help, and I'm sure he would be glad to give you that.
Nevertheless, since it is possible to answer the question, I will:
Input and Output in Haskell is only possible inside functions that evaluate an IO action (that is, a value of the IO type).
Of course, since main has type IO (), you can execute IO actions inside of it.
The simplest way to read a file is with the readFile function. It accepts a FilePath and evaluates to a IO String (which has the full contents of the file). I'll give you an example and I hope you can follow from it.
main :: IO ()
main = do
contents <- readFile "yourfilename.txt" -- because I used "<-", contents has type String
let fileLines = lines contents -- here I have a [String] with each line of the file
someFunction fileLines
return ()
someFunction should also evaluate an IO action, in this example. You can "put things inside" IO using return, in case you don't know.

Haskell Input to create a String List

I would like to allow a user to build a list from a series of inputs in Haskell.
The getLine function would be called recursively until the stopping case ("Y") is input, at which point the list is returned.
I know the function needs to be in a similar format to below. I am having trouble assigning the correct type signatures - I think I need to include the IO type somewhere.
getList :: [String] -> [String]
getList list = do line <- getLine
if line == "Y"
then return list
else getList (line : list)
So there's a bunch of things that you need to understand. One of them is the IO x type. A value of this type is a computer program that, when later run, will do something and produce a value of type x. So getLine doesn't do anything by itself; it just is a certain sort of program. Same with let p = putStrLn "hello!". I can sequence p into my program multiple times and it will print hello! multiple times, because the IO () is a program, as a value which Haskell happens to be able to talk about and manipulate. If this were TypeScript I would say type IO<x> = { run: () => Promise<x> } and emphatically that type says that the side-effecting action has not been run yet.
So how do we manipulate these values when the value is a program, for example one that fetches the current system time?
The most fundamental way to chain such programs together is to take a program that produces an x (an IO x) and then a Haskell function which takes an x and constructs a program which produces a y (an x -> IO y and combines them together into a resulting program producing a y (an IO y.) This function is called >>= and pronounced "bind". In fact this way is universal, if we add a program which takes any Haskell value of type x and produces a program which does nothing and produces that value (return :: x -> IO x). This allows you to use, for example, the Prelude function fmap f = (>>= return . f) which takes an a -> b and applies it to an IO a to produce an IO b.
So It is so common to say things like getLine >>= \line -> putStrLn (upcase line ++ "!") that we invented do-notation, writing this as
do
line <- getLine
putStrLn (upcase line ++ "!")
Notice that it's the same basic deal; the last line needs to be an IO y for some y.
The last thing you need to know in Haskell is the convention which actually gets these things run. That is that, in your Haskell source code, you are supposed to create an IO () (a program whose value doesn't matter) called Main.main, and the Haskell compiler is supposed to take this program which you described, and give it to you as an executable which you can run whenever you want. As a very special case, the GHCi interpreter will notice if you produce an IO x expression at the top level and will immediately run it for you, but that is very different from how the rest of the language works. For the most part, Haskell says, describe the program and I will give it to you.
Now that you know that Haskell has no magic and the Haskell IO x type just is a static representation of a computer program as a value, rather than something which does side-effecting stuff when you "reduce" it (like it is in other languages), we can turn to your getList. Clearly getList :: IO [String] makes the most sense based on what you said: a program which allows a user to build a list from a series of inputs.
Now to build the internals, you've got the right guess: we've got to start with a getLine and either finish off the list or continue accepting inputs, prepending the line to the list:
getList = do
line <- getLine
if line == 'exit' then return []
else fmap (line:) getList
You've also identified another way to do it, which depends on taking a list of strings and producing a new list:
getList :: IO [String]
getList = fmap reverse (go []) where
go xs = do
x <- getLine
if x == "exit" then return xs
else go (x : xs)
There are probably several other ways to do it.

How to get normal value from IO action in Haskell

I have the following function:
get :: Chars -> IO Chars
get cs = do
char <- getChar
let (dats, idx) = (curData cs, curIndex cs)
let (x,y:xs) = splitAt idx dats
let replacement = x ++ (ord char) : xs
return $ Chars replacement idx
and I'd like to get a Chars value out of it, not an IO action. I have no idea how to do this, or if it is even possible.
The Chars type is basically just a container:
data Chars = Chars {
curData :: [Int],
curIndex :: Int
-- etc.
}
The specifics aren't that important, I just want to know if there's a way for this function to return a Chars instead of an IO Chars.
If not, how do I pass this as an argument to a function that takes a Chars? I'm kind of new to Haskell I/O, but I don't think I want all of my functions that take Chars as arguments to instead have to take IO Chars as arguments, and then extract and repackage them. It seems unnecessary.
Thanks!
You can't, because that would violate referential transparency.
IO in Haskell is made this way exactly to distinguish between actions whose result and effects may vary depending on the interaction with the environment/user and pure functions whose results are not going to change when you call them with the same input parameters.
In order to pass the result to a pure function taking a Chars in input you have to call your IO action into another IO action, bind the result with the <- operator to a variable and pass it to your pure function. Pseudocode example:
myPureFunction :: Chars -> ...
otherAction :: Chars -> IO ()
otherAction cs = do
myChars <- get cs
let pureResult = myPureFunction myChars
...
If you're new to IO in haskell, you may wish to have a look at the Input and Output chapters in Learn You a Haskell for a Great Good! and Real World Haskell.
There is actually a way to simply get a pure value out of an IO action, but in your case you shouldn't do it, as you're interacting with the environment: the unsafe way is ok only when you can guarantee you're not violating referential transparency.
It's impossible (I lie, there is an extremely unsafe way to cheat your way out of it).
The point is that if any I/O is performed, the behaviour and result of your programme may not depend only on explicit arguments to the used functions, thus that must be declared in the type by having it IO something.
You use the result of an IO a action in a pure function by binding the result in main (or something called from main) and then applying the pure function, binding the result in a let,
cs ::Chars
cs = undefined
main = do
chars <- get cs
let result = pureFunction chars
print result
or, if the function you want to apply to chars has type Chars -> IO b
main = do
chars <- get cs
doSomething chars

Haskell IO (String) and String

I want to write functions and put result to string.
I want function:
read' :: FilePath -> String
I use:
:t readFile
readFile :: FilePath -> IO String
I make:
read' :: IO ()
read' = do
str <- readFile "/home/shk/workspace/src/test.txt"
putStrLn str
I want to ask str is string or not?
We know that:
:t putStrLn
putStrLn :: String -> IO ()
Then why i can't:
read' :: String
read' = do
str <- readFile "/home/shk/workspace/lxmpp/src/test.txt"
str
I get error that:
Couldn't match expected type `[t0]' with actual type `IO String'
In the return type of a call of `readFile'
In a stmt of a 'do' expression:
str <- readFile "/home/shk/workspace/lxmpp/src/test.txt"
In the expression:
do { str <- readFile "/home/shk/workspace/src/test.txt";
str }
Thank you.
Just to quibble a bit more, while the other answers are perfectly correct, I want to emphasize something: Something with the type IO String isn't just a string that the type system won't let you get at directly. It's a computation that performs I/O to get a string for you. Applying readFile to a file path doesn't return a String value any more than putting a steak next to a meat grinder magically turns them into a hamburger.
When you have some code like this:
foo = do let getStr = readFile "input.txt"
s1 <- getStr
s2 <- getStr
-- etc.
That doesn't mean you're "taking the string out of getStr twice". It means you're performing the computation twice and can easily get different results between the two.
I think no one has answered this, very important question, yet:
I want to ask str is string or not?
I will try to.
The type of the variable str is String, yes.
However, the scope of this variable is very limited. I think desugaring the do-notation is necessary for understanding:
read' = readFile "/home/shk/workspace/src/test.txt" >>= (\str -> putStrLn str)
I think here it becomes more clear why str is not good enough. It is an argument of the function you pass to >>=. Its value only becomes available when someone calls your function, which happens only when the IO action containing it is being executed.
Also, the type of read' :: IO () is determined not so much by the putStrLn str, but rather by the return type of the operator >>=. Have a look at it (specialized to the IO monad):
(>>=) :: IO a -> (a -> IO b) -> IO b
You can see that the result is always an IO b action, so trying to change any of arguments won't help.
You can read some monad tutorial if you want to understand why the type is the way it is. The intuition behind it is: you can't perform an action without performing an action.
And on the practical side of the question, to use the value returned by some action, instead of trying to do use (extractValue inputAction), which does not make sense because extractValue is not possible, try inputAction >>= use if your use does involve I/O, or fmap use inputAction if it does not.
You should use return str in read' if you want it to return str instead of (). You can't strip IO from the type of read', since it's not a pure function. To get a better grip on how input/output in Haskell works I recommend you to read a tutorial.
As a more detailed reason why: It allows impurity.
You absolutely can not perform IO during a pure operation, or it would completely break referential transparency. Technically you can use unsafePerformIO but it would break referential transparency in this case - you should only use that if you can guarantee that the result is always the same.

Resources