
From Learn You a Haskell ("Let it be" section):
1. cylinder :: (RealFloat a) => a -> a -> a 2. cylinder r h = 3. let sideArea = 2 * pi * r * h 4. topArea = pi * r ^2 5. in sideArea + 2 * topArea =================== What's the proper indentation for LET so these problems (below) don't arise? I thought LET and IN should be aligned in the same column. Also, isn't a LET expression an "expression." Michael ============== This works: import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber ============== This works: import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) putStrLn $ "Number is " ++ show randNumber ============== This doesn't: import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber [michael@localhost ~]$ runhaskell zz.hs zz.hs:4:2: The last statement in a 'do' construct must be an expression

You're running into this problem because you're in a do-block. In a do- block, all the continuing lines of a single statement in the do-block must be indented w/r/t to the first line. The cylinder example doesn't have this issue because it's not in a do-block. The layout rule (I'm summarizing and I haven't read the spec, so someone jump on me if I'm stating the wrong thing) basically says that within some implied { }, ;'s will be inserted for any line that is at the same indentation level as the previous line, e.g. import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber has the following implied symbols import System.Random main = do { gen <- getStdGen ; let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) ; in putStrLn $ "Number is " ++ show randNumber } which is now obviously wrong, as the let and in are in two separate statements. Conversely, import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber implied: import System.Random main = do { gen <- getStdGen ; let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber } Hope that clarifies things. -Ross On Oct 8, 2009, at 11:43 AM, michael rice wrote:
From Learn You a Haskell ("Let it be" section):
1. cylinder :: (RealFloat a) => a -> a -> a 2. cylinder r h = 3. let sideArea = 2 * pi * r * h 4. topArea = pi * r ^2 5. in sideArea + 2 * topArea ===================
What's the proper indentation for LET so these problems (below) don't arise? I thought LET and IN should be aligned in the same column. Also, isn't a LET expression an "expression."
Michael
==============
This works:
import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber
==============
This works:
import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) putStrLn $ "Number is " ++ show randNumber
==============
This doesn't:
import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber
[michael@localhost ~]$ runhaskell zz.hs
zz.hs:4:2: The last statement in a 'do' construct must be an expression
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Oct 8, 2009, at 11:43 , michael rice wrote:
This doesn't:
import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber
[michael@localhost ~]$ runhaskell zz.hs
zz.hs:4:2: The last statement in a 'do' construct must be an expression
The problem here is that the "do" construct parses things a bit differently; if you're at the same indentation level, it inserts a (>>) on the assumption that the next line is an independent expression, so you need to indent the "in" a bit more to avoid it. Note however that this usage is common enough that "do" provides a shorthand: you can simply omit the "in", and "let" will be parsed as if it were a statement:
main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int,StdGen) putStrLn $ "Number is " ++ show randNumber
-- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

On Oct 8, 2009, at 11:53 , Brandon S. Allbery KF8NH wrote:
The problem here is that the "do" construct parses things a bit differently; if you're at the same indentation level, it inserts a (>>) on the assumption that the next line is an independent expression, so you need to indent the "in" a bit more to avoid it.
I should probably clarify that what is actually inserted is a semicolon as noted by Ross Mellgren, and the next level of translation then replaces it with the (>>) operator.
Note however that this usage is common enough that "do" provides a shorthand: you can simply omit the "in", and "let" will be parsed as if it were a statement:
main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int,StdGen) putStrLn $ "Number is " ++ show randNumber
I should also mention that there are still constructs where this indentation quirk can bite you, notably:
if foo then bar else baz
works outside of a "do" construct, but inside one you need extra spaces:
do if foo then bar else baz
otherwise, you again get semicolons inserted before the "then" and "else" and subsequently replaced with (>>). -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH

Thanks all,
So, in a do expression
let x = 1
y = 2
etc.
in z = 1 + 2
if <bool expr>
then
etc.
else
etc.
Is this deviation documented somewhere?
Michael
--- On Thu, 10/8/09, Brandon S. Allbery KF8NH
main = do> gen <- getStdGen> let (randNumber, newGen) = randomR (1,6) gen :: (Int,StdGen)> putStrLn $ "Number is " ++ show randNumber -- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.comsystem administrator [openafs,heimdal,too many hats] allbery@ece.cmu.eduelectrical and computer engineering, carnegie mellon university KF8NH

I don't know of any offhand that specifically call it out -- it's a natural consequence of the layout rule which is described in the Haskell Report. However, there is at least one ticket in Haskell' to fix it for if/then/else: http://hackage.haskell.org/trac/haskell-prime/ticket/23 -Ross On Oct 8, 2009, at 1:03 PM, michael rice wrote:
Thanks all,
So, in a do expression
let x = 1 y = 2 etc. in z = 1 + 2
if <bool expr> then etc. else etc.
Is this deviation documented somewhere?
Michael
--- On Thu, 10/8/09, Brandon S. Allbery KF8NH
wrote: From: Brandon S. Allbery KF8NH
Subject: Re: [Haskell-cafe] Let it be To: "michael rice" Cc: "Brandon S. Allbery KF8NH" , haskell-cafe@haskell.org Date: Thursday, October 8, 2009, 11:53 AM On Oct 8, 2009, at 11:43 , michael rice wrote:
This doesn't:
import System.Random main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int, StdGen) in putStrLn $ "Number is " ++ show randNumber
[michael@localhost ~]$ runhaskell zz.hs
zz.hs:4:2: The last statement in a 'do' construct must be an expression
The problem here is that the "do" construct parses things a bit differently; if you're at the same indentation level, it inserts a (>>) on the assumption that the next line is an independent expression, so you need to indent the "in" a bit more to avoid it.
Note however that this usage is common enough that "do" provides a shorthand: you can simply omit the "in", and "let" will be parsed as if it were a statement:
main = do gen <- getStdGen let (randNumber, newGen) = randomR (1,6) gen :: (Int,StdGen) putStrLn $ "Number is " ++ show randNumber
-- brandon s. allbery [solaris,freebsd,perl,pugs,haskell] allbery@kf8nh.com system administrator [openafs,heimdal,too many hats] allbery@ece.cmu.edu electrical and computer engineering, carnegie mellon university KF8NH
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe

On Thu, Oct 8, 2009 at 2:07 PM, Ross Mellgren
there is at least one ticket in Haskell' to fix it for if/then/else
...and there isn't one for let/in because you can use just let (without in) inside a do-block. Of course the meanings are different as in the first case the let-bound variables scope only the 'in ...' part, while without in it scopes the rest of the do block: do {ini; let ... in ...; rest} => ini >> (let ... in ...) >> rest do {ini; let ...; ...; rest} => ini >> (let ... in (... >> rest)) HTH, -- Felipe.
participants (4)
-
Brandon S. Allbery KF8NH
-
Felipe Lessa
-
michael rice
-
Ross Mellgren