What is the reason for having let
in do
block.
-- codeblock A
main = do
let a = 0
let f a = a + 1
let b = f 0
print (a,b)
-- codeblock B
main = do
a = 0
f a = a + 1
b = f 0
print (a,b)
Assume all let
without in
must followed by =
(Is this true?)
Compiler should be able to imply let
from =
and preprocess/de-sugar codeblock B
to codeblock A
using let
in this case seem to be unnecessary, like you could write codeblock C
but choose to write codeblock D
-- codeblock C
main = do
print (a,b)
a = 0
f a = a + 1
b = f 0
-- codeblock D
main = do
print (a,b)
function a = 0
function f a = a + 1
function b = f 0
To clarify the my assumption doesn't not include let
that followed by in
which should leave untouched.
-- codeblock E
main = do
a = 0
f a = a + 1
b = f 0
c = let d = 1
e = 1
in d + e
print (a,b,c)
I don't know why it was done, but here is one reason I can imagine: it allows you to specify which bindings should be established sequentially, and which simultaneously, which can matter in the case of shadowing.
For example, imagine that your suggestion is implemented, and then consider:
foo :: [Int]
foo = do
x <- return [1]
y = 0:x
x = [1..5]
y
There are two reasonable ways to desugar this:
foo1 :: [Int]
foo1 = do
x <- return [1]
let y = 0:x
let x = [1..5]
y
foo2 :: [Int]
foo2 = do
x <- return [1]
let y = 0:x
x = [1..5]
y
foo1
evaluates to [0,1]
, and foo2
to [0,1,2,3,4,5]
. This is a strange way to write your code, surely, but the fact that the let
is required to be explicit means there is no ambiguity as to what you intend.
As noted in the comments by chi, shadowing is not the only reason you might need to be explicit about how your let
bindings are grouped: a function definition might require multiple equations, to match multiple parameter patterns.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With