As I am working on learning Haskell, I understand it is a purely functional language. I am having trouble understanding why let-statements don't violate purity.
For example (in ghci):
Prelude> let e = exp 1
Prelude> e
2.718281828459045
Prelude> let e = 2
Prelude> e
2
isn't my second let statement producing a side effect? Or is the second let statement a new closure?
Your second let creates a new binding for e that shadows the existing variable. It does not modify e. You can easily check this with the following:
Prelude> let e = 1
Prelude> let f () = "e is now " ++ show e
Prelude> f ()
"e is now 1"
Prelude> let e = 2
Prelude> e
2
Prelude> f ()
"e is now 1"
Prelude>
let introduces a new local variable with a single unalterable value, and it has more local scope than any surrounding definitions, so for example:
*Main> (let length = 2 in show length) ++ ' ':show (length "Hello")
"2 5"
Here the first length has the value 2, but its scope local to the brackets. Outside the brackets, length means what it has always meant. Nothing has been edited, just a more local variable has been introduced that happens to have the same name as another one in a different scope. Let's make ghci mad by omitting the brackets and making it try to make length a number and a function:
*Main> let length = 2 in show length ++ ' ':show (length "Hello")
<interactive>:1:14:
No instance for (Num ([Char] -> a0))
arising from the literal `2'
Possible fix: add an instance declaration for (Num ([Char] -> a0))
In the expression: 2
In an equation for `length': length = 2
In the expression:
let length = 2 in show length ++ ' ' : show (length "Hello")
<interactive>:1:19:
No instance for (Show ([Char] -> a0))
arising from a use of `show'
Possible fix: add an instance declaration for (Show ([Char] -> a0))
In the first argument of `(++)', namely `show length'
In the expression: show length ++ ' ' : show (length "Hello")
In the expression:
let length = 2 in show length ++ ' ' : show (length "Hello")
And here's your example:
*Main> let e = exp 1 in show e ++ " " ++ let e = 2 in show e
"2.718281828459045 2"
I'll add brackets to emphasise the scope:
*Main> let e = exp 1 in (show e ++ " " ++ (let e = 2 in (show e)))
"2.718281828459045 2"
The first e is hidden rather than edited. Referential transparency is preserved, but it's definitely bad practice because it's hard to follow.
Now secretly the interactive prompt is a bit like one big do block in the IO monad, so let's look at that:
testdo = do
let e = exp 1
print e
let e = 2
print e
Now I have to admit that looks an awful lot like breaking referential transparency, but bear in mind that this looks like it does too:
testWrite = do
writeFile "test.txt" "Hello Mum"
xs <- readFile "test.txt"
print xs
writeFile "test.txt" "Yo all"
xs <- readFile "test.txt"
print xs
Now in what sense have we got referential transparency? xs clearly refers to two different strings. Well, what does this do notation actually mean? It's syntactic sugar for
testWrite = writeFile "test.txt" "Hello Mum"
>> readFile "test.txt"
>>= (\xs -> print xs
>> writeFile "test.txt" "Yo all"
>> readFile "test.txt"
>>= (\xs -> print xs))
Now it's clearer that what looks like assignment is just local scope again. You presumably are happy to do
increment :: [Int] -> [Int]
increment = \x -> map (\x -> x+1) x
Which is doing the same thing.
Summary
What appeared to be assignment is just introduction of a new local scope. Phew. If you use this a lot, you make it very unclear what your code means.
Related
I am a beginner in Haskell and I wanted to know if it was possible to print individual elements of a given list. I tried solving this problem but have failed. This is the code:
main :: IO()
main = do
let list = [1,2,3]
let size = length list
let temp = print_elem list size
print temp
print_elem :: [Int] -> Int -> Int
print_elem xs x = do
let size = length xs
let idx = size - x
let element = xs !! idx
putStr (show element)
putStr(" ")
let dummy = print_elem (xs (x-1))
return " "
I wanted to print something like this
1 2 3
If I simply use putStr (show list) it will display [1,2,3] and I don't want that.
But when I run this code multiple errors occur
printelem.hs:14:5: error:
* Couldn't match expected type `Int' with actual type `IO b0'
* In a stmt of a 'do' block: putStr (show element)
In the expression:
do let size = length xs
let idx = size - x
let element = xs !! idx
putStr (show element)
....
In an equation for `print_elem':
print_elem xs x
= do let size = ...
let idx = ...
let element = ...
....
|
14 | putStr (show element)
| ^^^^^^^^^^^^^^^^^^^^^
printelem.hs:16:29: error:
* Couldn't match expected type `Int -> [Int]'
with actual type `[Int]'
* The function `xs' is applied to one argument,
but its type `[Int]' has none
In the first argument of `print_elem', namely `(xs (x - 1))'
In the expression: print_elem (xs (x - 1))
|
16 | let dummy = print_elem (xs (x-1))
| ^^^^^^^^
How do you fix this issue?
You are thinking too imperatively. First, you need a list of strings, not a list of integers. That's map:
> map show [1,2,3]
["1","2","3"]
Next, you want to join them into a single, space-separate string. That's Data.List.intercalate:
> import Data.List
> intercalate " " (map show [1,2,3])
"1 2 3"
which you can then pass to putStrLn (print would give you the string representation of the string you already have):
import Data.List
main :: IO()
main = do
let list = [1,2,3]
putStrLn (intercalate " " (map show list))
One way to do it is
> mapM_ putStr (intersperse " " (map show [1,2,3])) >> putStrLn ""
1 2 3
it :: ()
This prints the elements one by one, converted to strings by show, interspersed with the spaces in between.
> intersperse " " (map show [1,2,3])
["1"," ","2"," ","3"]
it :: [[Char]]
mapM_ maps an IO action constructor putStr :: String -> IO () on each of the elements, and executes them all as one combined sequence of actions.
Finally this code prints the newline.
I'm trying to implement a function which converts a string to a list of Maybe Ints, e.g. readInts "1 2 42 foo" = [Just 1,Just 2,Just 42,Nothing].
My first aproach was:
readInts (s::String) = do {
ws <- words s;
return (map (readMaybe::(String -> Maybe Int)) ws)
}
This resulted in the following error:
lab_monad.hs:20:52:
Couldn't match type ‘Char’ with ‘[Char]’
Expected type: [String]
Actual type: String
In the second argument of ‘map’, namely ‘ws’
In the first argument of ‘return’, namely
‘(map (readMaybe :: String -> Maybe Int) ws)’
Failed, modules loaded: none.
What I tried next (and worked), was:
readInts (s::String) = do {
let ws = (words s) in do
return (map (readMaybe::(String -> Maybe Int)) ws)
}
My question here is, words s obviously is of type [String]. Why does the interpreter say it is a String? What am I not understanding about <- operator?
ws <- words s, in the list monad, nondeterministically assigns one word from words s to ws; the remaining code simply works with that one word, and the return function "magically" combines the results of working on all the words into the result list.
readInts s = do
ws <- words s -- ws represents *each* word in words s
return (readMaybe ws)
The do notation is just syntactic sugar for using monadic bind:
readInts s = words s >>= (\ws -> return (readMaybe ws))
Without using the Monad instance for lists, you can use map to apply the same function to each word.
readInts s = map readMaybe (words s)
let, on the other hand, simply provides a name for a more complicated expression to be used in another expression. It can be considered syntactic sugar for defining and immediately applying an anonymous function. That is,
let x = y + z in f x
is equivalent to
(\x -> f x) (y + z)
^ ^ ^
| | |
| | RHS of let binding
| part after "in"
LHS of let binding
A let statement with multiple bindings is equivalent to nested let statements:
let x = y + z
a = b + c
in x + a
is equivalent to
let x = y + z
in let a = b + c
in x + a
which desugars to
(\x -> (\a -> x + a)(b + c))(y + z)
I tried break line using \n, putStrLn and print but nothing works.
When I use \n the result only concatenates the strings, and when I use putStrLn or print I receive a type error.
Output for \n:
formatLines [("a",12),("b",13),("c",14)]
"a...............12\nb...............13\nc...............14\n"
Output for putStrLn:
format.hs:6:22:
Couldn't match type `IO ()' with `[Char]'
Expected type: String
Actual type: IO ()
In the return type of a call of `putStrLn'
In the expression:
putStrLn (formatLine ((fst x), (snd x)) ++ formatLines xs)
In an equation for `formatLines':
formatLines (x : xs)
= putStrLn (formatLine ((fst x), (snd x)) ++ formatLines xs)
Failed, modules loaded: none.
the output for print is the same as that of putStrLn
Here is my code:
formatLine :: (String,Integer) -> String
formatLine (s, i) = s ++ "..............." ++ show i
formatLines::[(String,Integer)] -> String
formatLines [] = ""
formatLines (x:xs) = print (formatLine ((fst x), (snd x)) ++ formatLines xs)
I understand the reason of the error for print and putStrLn but i have no idea how fix it.
Split your code in two parts.
One part simply constructs the string. Use "\n" for newlines.
The second part takes the string and applies putStrLn (NOT print) to it. The newlines will get printed correctly.
Example:
foo :: String -> Int -> String
foo s n = s ++ "\n" ++ show (n*10) ++ "\n" ++ s
bar :: IO ()
bar = putStrLn (foo "abc" 42)
-- or putStr (...) for no trailing newline
baz :: String -> IO ()
baz s = putStrLn (foo s 21)
If you use print instead, you'll print the string representation, with quotes and escapes (like \n) inside it. Use print only for values that have to be converted to string, like numbers.
Also note that you can only do IO (like printing stuff) in functions whose return type is IO (something).
You need to print the results to output.
This is an IO action, and so you cannot have a function signature ending with -> String. Instead, as #chi points out, the return type should be IO (). Further, since you have the function to generate formatted string already, all you need is a function to help you map the printing action over your input list. This you can do using mapM_, like so:
formatLines::[(String,Integer)] -> IO ()
formatLines y = mapM_ (putStrLn . formatLine) y
Demo
So I'm trying to make a little program that can take in data captured during an experiment, and for the most part I think I've figured out how to recursively take in data until the user signals there is no more, however upon termination of data taking haskell throws Exception: <<loop>> and I can't really figure out why. Here's the code:
readData :: (Num a, Read a) => [Point a] -> IO [Point a]
readData l = do putStr "Enter Point (x,y,<e>) or (d)one: "
entered <- getLine
if (entered == "d" || entered == "done")
then return l
else do let l = addPoint l entered
nl <- readData l
return nl
addPoint :: (Num a, Read a) => [Point a] -> String -> [Point a]
addPoint l s = l ++ [Point (dataList !! 0) (dataList !! 1) (dataList !! 2)]
where dataList = (map read $ checkInputData . splitOn "," $ s) :: (Read a) => [a]
checkInputData :: [String] -> [String]
checkInputData xs
| length xs < 2 = ["0","0","0"]
| length xs < 3 = (xs ++ ["0"])
| length xs == 3 = xs
| length xs > 3 = ["0","0","0"]
As far as I can tell, the exception is indication that there is an infinite loop somewhere, but I can't figure out why this is occurring. As far as I can tell when "done" is entered the current level should simply return l, the list it's given, which should then cascade up the previous iterations of the function.
Thanks for any help. (And yes, checkInputData will have proper error handling once I figure out how to do that.)
<<loop>> basically means GHC has detected an infinite loop caused by a value which depends immediately on itself (cf. this question, or this one for further technical details if you are curious). In this case, that is triggered by:
else do let l = addPoint l entered
This definition, which shadows the l you passed as an argument, defines l in terms of itself. You meant to write something like...
else do let l' = addPoint l entered
... which defines a new value, l', in terms of the original l.
As Carl points out, turning on -Wall (e.g. by passing it to GHC at the command line, or with :set -Wall in GHCi) would make GHC warn you about the shadowing:
<interactive>:171:33: warning: [-Wname-shadowing]
This binding for ‘l’ shadows the existing binding
bound at <interactive>:167:10
Also, as hightlighted by dfeuer, the whole do-block in the else branch can be replaced by:
readData (addPoint l entered)
As an unrelated suggestion, in this case it is a good idea to replace your uses of length and (!!) with pattern matching. For instance, checkInputData can be written as:
checkInputData :: [String] -> [String]
checkInputData xs = case xs of
[_,_] -> xs ++ ["0"]
[_,_,_] -> xs
_ -> ["0","0","0"]
addPoint, in its turn, might become:
addPoint :: (Num a, Read a) => [Point a] -> String -> [Point a]
addPoint l s = l ++ [Point x y z]
where [x,y,z] = (map read $ checkInputData . splitOn "," $ s) :: (Read a) => [a]
That becomes even neater if you change checkInputData so that it returns a (String, String, String) triple, which would better express the invariant that you are reading exactly three values.
I am currently working 99 haskell problems
I cannot understand why am I getting an error in this function :-
repli :: [a] -> Int -> [a]
repli xs n = concatMap (take n . repeat) xs
If you are using the REPL, try
>>> let repli xs n = concatMap (take n . repeat) xs
Writing Haskell in the REPL (ake GHCi) is a bit different to writing it in a file. For one thing, variable bindings and function definitions have to be prefixed with let as in
>>> let a = 1
>>> let f x = x + a
For another, you generally have to enter definitions all on one line. You can separate separate definitions with a semicolon, like this
>>> let a = 1; b = 2
or you can use multi-line mode, like this
>>> :{
>>> let c = 3
>>> d = 4
>>> :}
If you've learnt about monads (have you?) then you can imagine that everything you write in the REPL is part of a do block which is of type IO (), with the statements executed as you type them. So in a file you might write
main :: IO ()
main = do
name <- getLine
let greeting = "Hello " ++ name ++ "!"
putStrLn greeting
whereas in the REPL you would write
>>> name <- getLine
Chris
>>> let greeting = "Hello " ++ name ++ "!"
>>> putStrLn greeting
Hello Chris!
>>>