Difference between where bindings, let bindings and the single assignment operator (<-)
Asked Answered
O

1

15

I do not understand the difference between the three syntaxes:

  • where a = f (b)
  • do a <- f (b)
  • do let a = f (b)

I do understand somehow though that a <- f(b) is different from the other two, in most cases where I tried all three worked. Also I read somewhere on the net that per block you should try to get along with one let binding only in order to be "idiomatic". But I never seem to manage.

How do I decide what to use?

Oar answered 13/2, 2012 at 0:25 Comment(6)
You must have tried wrong. I can think of almost no scenario where changing a <- f b to let a = f b (or vice versa) wouldn't break your code. That is assuming you're actually using a afterwards.Paresthesia
@Paresthesia yes, but you can easily refactor your code to make it work. Same thing with let and where, "minor" refactoring is required. As I mentioned, I am aware that the single assignment operator is somehow different. Still I like to know what the difference is.Oar
No, you can't. If f b has a non-monadic type a <- f b will simply not compile. There's no refactoring you can do to make it compile (unless you count stuffing a return in there to be refactoring...). If f b does have a monadic type, let a = f b will cause a to have the same type. This will almost certainly break the code using a.Paresthesia
@Paresthesia Not sure if I can "compete" with you on this, but isn't it if I stay in the same monad it should not matter?Oar
I'm not sure what you mean by that. If you have multiple <-s inside a do-block, their respective right operands must be in the same monad. But that has nothing to do with let.Paresthesia
Similar question: https://mcmap.net/q/158882/-haskell-where-vs-let/596361Statuesque
P
27

let foo = bar in ... simply defines foo to be the exact same thing as bar within the context of ...; you could simply use textual substitution to replace all uses of foo in ... with (bar) and get the exact same result.

where clauses are similar to let...in expressions, but go at the end of a function clause, instead of being an expression. For instance,

foo x
    | p1 = ... y ...
    | p2 = ... y ...
  where
    y = ...

There is no way to rewrite this with let...in without changing the guards into if...then...elses. Often, where clauses are used over let...in clauses purely for reasons of style.

The bind operator is something different entirely. It's used in do notation to "extract" a value from a monadic computation. That is, if foo has the type m a, then after x <- foo, x has the type a. All the other "binding" forms just define names, but <- is used for binding a computation's result to a name from within a monad. <- can only be used inside a do block, so it's exclusively used to build up a larger computation in the same monad as the action you're binding the result of.

let foo = bar in do notation is just a convenience; you can rewrite:

do let foo = bar
   ...

as

let foo = bar
in do ...

but that gets messy when you have a lot of such bindings, as the do blocks get nested deeper and deeper.

I don't know what the advice you mentioned is talking about; you can define multiple variables in a let...in block just fine, and

let foo = ...
    bar ...
in ...

is more idiomatic than

let foo = ...
in let bar = ...
   in ...
Pannell answered 13/2, 2012 at 0:37 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.