Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Equal (=) Vs left arrow (<-) symbols in haskell

Tags:

haskell

People also ask

What does -> mean in Haskell?

(->) is often called the "function arrow" or "function type constructor", and while it does have some special syntax, there's not that much special about it. It's essentially an infix type operator. Give it two types, and it gives you the type of functions between those types.

What does the arrow do in Haskell?

The Arrow (either (->) or MyArr ) is an abstraction of a computation. For a function b -> c , b is the input and c is the output. For a MyArr b c , b is the input and c is the output.

What does <$> mean in Haskell?

It's merely an infix synonym for fmap , so you can write e.g. Prelude> (*2) <$> [1.. 3] [2,4,6] Prelude> show <$> Just 11 Just "11" Like most infix functions, it is not built-in syntax, just a function definition. But functors are such a fundamental tool that <$> is found pretty much everywhere.

Do expressions Haskell?

As a syntactical convenience, do notation does not add anything essential, but it is often preferable for clarity and style. However, do is not needed for a single action, at all. The Haskell "Hello world" is simply: main = putStrLn "Hello world!"


To understand the real difference, you have to understand monads, and the desugaring described by @rightfold in their answer.

For the specific case of the IO monad, as in your getArgs example, a rough but useful intuition can be made as follows:

  • x <- action runs the IO action, gets its result, and binds it to x
  • let x = action defines x to be equivalent to action, but does not run anything. Later on, you can use y <- x meaning y <- action.

Programmers coming from imperative languages which allow closures, may draw this rough parallel comparison with JavaScript:

var action = function() { print(3); return 5; }

// roughly equivalent to x <- action
print('test 1')
var x = action()  // output:3
// x is 5

// roughly equivalent to let y = action
print('test 2')
var y = action    // output: nothing
// y is a function

// roughly equivalent to z <- y
print('test 3')
var z = y()       // output:3
// z is 5

Again: this comparison focuses on IO, only. For other monads, you need to check what >>= actually is, and think about the desugaring of do.


do
    x <- y
    f x

is equivalent to:

y >>= \x -> f x

do
    let x = y
    f x

is equivalent to

f y

i.e. let/= does no monadic bind whereas <- does.


The code doesn't compile because the types don't match. Let's load up a GHCI session and look at the types of the functions you're using -

> :t writeFile
writeFile :: FilePath -> String -> IO ()
>
> :t readFile
readFile :: FilePath -> IO String

So writeFile wants a FilePath and a String. You want to get the String from readFile - but readFile returns IO String instead of String.

Haskell is a very principled language. It has a distinction between pure functions (which give the same outputs every time they are called with the same arguments) and impure code (which may give different results, e.g. if the function depends on some user input). Functions that deal with input/output (IO) always have a return type which is marked with IO. The type system ensures that you cannot use impure IO code inside pure functions - for example, instead of returning a String the function readFile returns an IO String.

This is where the <- notation is important. It allows you to get at the String inside the IO and it ensures that whatever you do with that string, the function you are defining will always be marked with IO. Compare the following -

> let x = readFile "tmp.txt"
> :t x
x :: IO String

which isn't what we want, to this

> y <- readFile "tmp.txt"
> :t y
y :: String

which is what we want. If you ever have a function that returns an IO a and you want to access the a , you need to use <- to assign the result to a name. If your function doesn't return IO a , or if you don't want to get at the a inside the IO then you can just use =.


let x = readFile file1

This takes the action "readFile file1" and stores the action in x.

x <- readFile file1

This executes the action "readFile file1" and stores the result of the action in x.

In the first example, x is an unexecuted I/O action object. In the second example, x is the contents of a file on disk.