Difference between let and <-

While learning Haskell, I came across these kinds of lines

manager <- newManager tlsManagerSettings
let request = setRequestManager manager "http://httpbin.org/get"

From my observation both assign value to these two variables, so what’s the difference and what’s the use case?


<- is valid in do-notation and it doesn’t just assign a value but also “unwraps” or “extracts” it from a monad.

Let us look at a Maybe Int example. We will take Just 1 and increment it by 1.

Here is the first attempt with let:

onePlusOne :: Maybe Int
onePlusOne = do
    let valueInMaybe = Just 1
    Just (valueInMaybe + 1)

This code won’t compile obviously, because let valueInMaybe = Just 1 is just a binding; valueInMaybe is Just 1 and I can’t add one to it, Just 1 isn’t of the same type as 1, it isn’t an Int.

But consider:

onePlusOne :: Maybe Int
onePlusOne = do
    valueInMaybe <- Just 1
    Just (valueInMaybe + 1)

This code compiles and returns Just 2. valueInMaybe <- Just 1 extracts 1 from Just 1 and binds it to valueInMaybe. In the next line, we sum 2 integers, valueInMaybe and 1. Finally we wrap the result 2 back into a Maybe. And we have to, because do-notation is a jail, we can’t escape from; if we’re working with a specific monad, we have to give this monad back at the end.

So what happens if valueInMaybe <- Nothing? The next line won’t be executed and the function will return Nothing. This is the reason why we have to return Maybe Int at the end and not just Int, because the result can be Nothing.


To bring this back to the example in the question:

If you look at the type newManager :: ManagerSettings -> IO Manager you see that newManager applied to tlsManagerSettings will produce a value of type IO Manager. Haskell cannot automatically ‘cast’ this type to Manager, so you have to manually indicate that you want this value to be unwrapped by using the <- symbol in do notation.

The type setRequestManager :: Manager -> Request -> Request for setRequestManager on the second line indicates that this is a pure function. Applying setRequestManager to manager and "http://httpbin.org/get" produces a value of type Request which does not need unwrapping.


Thanks! @belka @jaror It’s pretty clear to me now :ok_hand: :grin: