Recently, I started learning Haskell because I wanted to broaden my knowledge on functional programming and I must say I am really loving it so far. The resource I am currently using is the course 'Haskell Fundamentals Part 1' on Pluralsight. Unfortunately I have some difficulty understanding one particular quote of the lecturer about the following code and was hoping you guys could shed some light on the topic.
Accompanying Code
helloWorld :: IO ()
helloWorld = putStrLn "Hello World"
main :: IO ()
main = do
helloWorld
helloWorld
helloWorld
The Quote
If you have the same IO action multiple times in a do-block, it will be run multiple times. So this program prints out the string 'Hello World' three times. This example helps illustrate that putStrLn is not a function with side effects. We call the putStrLn function once to define the helloWorld variable. If putStrLn had a side effect of printing the string, it would only print once and the helloWorld variable repeated in the main do-block wouldn't have any effect.
In most other programming languages, a program like this would print 'Hello World' only once, since the printing would happen when the putStrLn function was called. This subtle distinction would often trip up beginners, so think about this a bit, and make sure you understand why this program prints 'Hello World' three times and why it would print it only once if the putStrLn function did the printing as a side effect.
What I don't understand
For me it seems almost natural that the string 'Hello World' is printed three times. I perceive the helloWorld variable (or function?) as a sort of callback which is invoked later. What I don't understand is, how if putStrLn had a side effect, it would result in the string being printed only once. Or why it would only be printed once in other programming languages.
Let's say in C# code, I would presume it would look like this:
C# (Fiddle)
using System;
public class Program
{
public static void HelloWorld()
{
Console.WriteLine("Hello World");
}
public static void Main()
{
HelloWorld();
HelloWorld();
HelloWorld();
}
}
I am sure I am overlooking something quite simple or misinterpret his terminology. Any help would be greatly appreciated.
EDIT:
Thank you all for your answers! Your answers helped me get a better understanding of these concepts. I don't think it fully clicked yet, but I will revisit the topic in the future, thank you!
It’d probably be easier to understand what the author means if we define helloWorld as a local variable:
main :: IO ()
main = do
let helloWorld = putStrLn "Hello World!"
helloWorld
helloWorld
helloWorld
which you could compare to this C#-like pseudocode:
void Main() {
var helloWorld = {
WriteLine("Hello World!")
}
helloWorld;
helloWorld;
helloWorld;
}
I.e. in C# WriteLine is a procedure that prints its argument and returns nothing. In Haskell, putStrLn is a function that takes a string and gives you an action that would print that string were it to be executed. It means that there is absolutely no difference between writing
do
let hello = putStrLn "Hello World"
hello
hello
and
do
putStrLn "Hello World"
putStrLn "Hello World"
That being said, in this example the difference isn’t particularly profound, so it’s fine if you don’t quite get what the author is trying to get at in this section and just move on for now.
it works a bit better if you compare it to python
hello_world = print('hello world')
hello_world
hello_world
hello_world
The point here being that IO actions in Haskell are “real” values that don’t need to be wrapped in further “callbacks” or anything of the sort to prevent them from executing - rather, the only way to do get them to execute is to put them in a particular place (i.e. somewhere inside main or a thread spawned off main).
This isn’t just a parlour trick either, this does end up having some interesting effects on how you write code (for example, it’s part of the reason why Haskell doesn’t really need any of the common control structures you’d be familiar with from imperative languages and can get away with doing everything in terms of functions instead), but again I wouldn’t worry too much about this (analogies like these don’t always immediately click)
It might be easier to see the difference as described if you use a function that actually does something, rather than helloWorld. Think of the following:
add :: Int -> Int -> IO Int
add x y = do
putStrLn ("I am adding " ++ show x ++ " and " ++ show y)
return (x + y)
plus23 :: IO Int
plus23 = add 2 3
main :: IO ()
main = do
_ <- plus23
_ <- plus23
_ <- plus23
return ()
This will print out "I am adding 2 and 3" 3 times.
In C#, you might write the following:
using System;
public class Program
{
public static int add(int x, int y)
{
Console.WriteLine("I am adding {0} and {1}", x, y);
return x + y;
}
public static void Main()
{
int x;
int plus23 = add(2, 3);
x = plus23;
x = plus23;
x = plus23;
return;
}
}
Which would print only once.
If the evaluation of putStrLn "Hello World" had side effects, then then message would only be printed once.
We can approximate that scenario with the following code:
import System.IO.Unsafe (unsafePerformIO)
import Control.Exception (evaluate)
helloWorld :: ()
helloWorld = unsafePerformIO $ putStrLn "Hello World"
main :: IO ()
main = do
evaluate helloWorld
evaluate helloWorld
evaluate helloWorld
unsafePerformIO takes an IO action and "forgets" it's an IO action, unmooring it from the usual sequencing imposed by the composition of IO actions and letting the effect take place (or not) according to the vagaries of lazy evaluation.
evaluate takes a pure value and ensures that the value is evaluated whenever the resulting IO action is evaluated—which for us it will be, because it lies in the path of main. We are using it here to connect the evaluation of some values to the exection of the program.
This code only prints "Hello World" one time. We treat helloWorld as a pure value. But that means it will be shared between all evaluate helloWorld calls. And why not? It's a pure value after all, why recalculate it needlessly? The first evaluate action "pops" the "hidden" effect and the later actions just evaluate the resulting (), which doesn't cause any further effects.
There is one detail to notice: you call putStrLn function only once, while defining helloWorld. In main function you just use the return value of that putStrLn "Hello, World" three times.
The lecturer says that putStrLn call has no side effects and it's true. But look at the type of helloWorld - it is an IO action. putStrLn just creates it for you. Later, you chain 3 of them with the do block to create another IO action - main. Later, when you execute your program, that action will be run, that's where side effects lie.
The mechanism that lies in base of this - monads. This powerful concept allows you to use some side effects like printing in a language that doesn't support side effects directly. You just chain some actions, and that chain will be run on start of your program. You will need to understand that concept deeply if you want to use Haskell seriously.
I've noticed some really strange behaviour:
I'm trying to get the haskell z3 bindings to work on windows. But if I insert code from the library into the Main module putStrLn stops working. How can that be?
The Setup
I used the precompiled x64 binary of the z3 solver (z3.4.8.4) for windows (and put it in C:\mylibs\z3-4.8.4_x64).
Then I created a stack project haskellZ3Test
The relevant parts from package.yaml:
dependencies:
- base >= 4.7 && < 5
- z3 <= 408.0
The relevant parts from stack.yaml:
resolver: lts-13.28
packages:
- .
- location:
git: https://github.com/IagoAbal/haskell-z3.git
commit: b10e09b8a809fb5bbbb1ef86aeb62109ece99cae
extra-dep: true
extra-include-dirs:
- "C:\\mylibs\\z3-4.8.4_x64\\include"
extra-lib-dirs:
- "C:\\mylibs\\z3-4.8.4_x64\\bin"
The Problem
Here is Main version 1:
module Main where
import Z3.Monad
main :: IO ()
main = putStrLn "hello world"
If I stack build and then stack exec haskellZ3Test-exe I get hello world on the command line as expected.
However - here is Main version 2:
module Main where
import Z3.Monad
ast :: Z3 AST
ast = do
a <- mkFreshBoolVar "A"
b <- mkFreshBoolVar "B"
mkImplies a b
main :: IO ()
main = putStrLn "hello world"
If I stack build and then stack exec haskellZ3Test-exe now I get nothing ...
Honestly I'm pretty puzzled - compiling and linking seems to work fine.
Any help is highly appreciated. Many thanks in advance.
I am using the Leksah IDE 0.15.0.1 and I get a warning when compiling the example package: "name ... found in source file but was not in scope".
What is the meaning of this Warning ?
I googled for this text but found noting enlightening.
The problem probably lies within the lines
testMain = do
allPass <- $quickCheckAll
unless allPass exitFailure
According to the QuickCheck documentation, in order to utilize quickCheckAll, the IO action that performs $quickCheckAll must have return [] before its definition.
To use quickCheckAll, add a definition to your module along the lines of
return []
runTests = $quickCheckAll
and then execute runTests.
So applying it to your testMain definition, it would end up being
return []
testMain = do
allPass <- $quickCheckAll
unless allPass exitFailure
The documentation also provides an explanation for such need:
Note: the bizarre return [] in the example above is needed on GHC 7.8 and > later; without it, quickCheckAll will not be able to find any of the
properties. For the curious, the return [] is a Template Haskell
splice that makes GHC insert the empty list of declarations at that
point in the program; GHC typechecks everything before the return []
before it starts on the rest of the module, which means that the later
call to quickCheckAll can see everything that was defined before the
return []. Yikes!
I am totally newbie to haskell.
I have such snippet code
lucky:: Int->String
lucky 7 = "LUCKY NUMBER SEVEN!"
lucky x = "Sorry , youre out of luck pal!"
I tried to input to terminal directly, seems not right.
But if I want to put this in file and load this file, then call lucky function. How should I construct this file?
Thank you!
I tried this:
module Main where
lucky:: Int->String
lucky 7 = "LUCKY NUMBER SEVEN!"
lucky x = "Sorry , youre out of luck pal!"
main = do
But when i try to call lucky in terminal, i got this:
factorial.hs:7:8: Empty 'do' construct
lucky 7
<interactive>:1:1: Not in scope: `lucky'
The problem is that your last line, main = do, is a syntax error; if your file has a syntax error then none of the functions in it will load. It should work fine if you take out your definition of main and try to load it.
On an unrelated note, generally Haskell type signatures are written with spacing like lucky :: Int -> String.
You don't need the module Main where or main = do lines.
You can load and use function definitions into ghci, simply by saving the file with a ".hs" extension, then typing :load and the the filename
a very simple question from a Haskell learner. I am working through Yet Another Haskell Tutorial and I am stuck on a simple syntax practice. The code given below: When I copy and paste it (from pdf) and then adjust the indentation it works fine, but when I type it out into an editor (in my case Notepad++) then it throws the following error:
Guess.hs:8:9: parse error on input ´hSetBuffering´
I made sure that I did not mix tabs and whitespaces (4 whitespaces) and I did not find a typo in the book. I am sure it is a very simple mistake so thanks for any input.
Nebelhom
Here is the code:
module Main
where
import IO
import Random
main = do
hSetBuffering stdin LineBuffering
num <- randomRIO (1::Int, 100)
putStrLn "I'm thinking of a number between 1 and 100"
doGuessing num
doGuessing num = do
putStrLn "Enter your guess:"
guess <- getLine
let guessNum = read guess
if guessNum < num
then do putStrLn "Too low!"
doGuessing num
else if read guess > num
then do putStrLn "Too high!"
doGuessing num
else do putStrLn "You Win!"
There's no syntax error that I can see, or reproduce:
$ ghci A.hs
GHCi, version 7.0.3: http://www.haskell.org/ghc/ :? for help
Loading package ghc-prim ... linking ... done.
Loading package integer-gmp ... linking ... done.
Loading package base ... linking ... done.
Loading package ffi-1.0 ... linking ... done.
[1 of 1] Compiling Main ( A.hs, interpreted )
Ok, modules loaded: Main.
which means it is probably tabs. Did you insert a tab somewhere? And then use 4 spaces for indenting? Tabs are in general a bad idea in Haskell, as they lead to unintentional, and unintelligible, syntax errors.