2

This simple program prints the sum and product of a list of numbers from an input file.

main :: IO ()
main = do
  text <- readFile "./input.txt"
  print $ sum [read line | line <- lines text]
  print $ product [read line | line <- lines text]

{-

Example input:
1
2
3
4

Example output:
10
24

-}

It would be nice to avoid repeating [read line | line <- lines text]. If I extract the function printSumAndProduct, I can use a let expression to do this:

main :: IO ()
main = do
  text <- readFile "./input.txt"
  printSumAndProduct text

printSumAndProduct :: String -> IO ()
printSumAndProduct text =
  let numbers = [read line | line <- lines text]
  in do
    print $ sum numbers
    print $ product numbers

However, I would prefer to just have one function, main. Is it possible to do this without having a separate function printSumAndProduct?

Here's what I've tried:

main :: IO ()
main = do
  text <- readFile "./input.txt"
  let numbers = [read line | line <- lines text]
  in do
    print $ sum numbers
    print $ product numbers

But this is a syntax error:

main.hs:5:3: error: parse error on input ‘in’
Pandu
  • 1,606
  • 1
  • 12
  • 23
  • 2
    (perhaps there is a better duplicate - but the key point is shown in point "2" of the accepted answer: inside `do`, you can simply use a `let` "statement" with no `in`) – Robin Zigmond Aug 20 '21 at 23:38
  • Ah -- I think so, thanks! – Pandu Aug 20 '21 at 23:38
  • 3
    Just insert one more space to the left of your last "in" keyword, and it compiles. There is some subtlety in the indenting rules. Seems in that case (do construct) the "in" clause has to be subordinate to the "let" clause. See answer by user395760 in the duplicated question. – jpmarinier Aug 21 '21 at 00:12
  • 5
    @jpmariner It's nothing to do with the `in` being subordinate to the `let` clause; the overall `let ... in` expression is not sensitive to indentation (the block of bindings inside it is, but the indentation if the `in` part doesn't matter). What's going wrong is that every line that is indented to match the alignment of the statements in the `do` block is taken as beginning a *new* statement in the block, and a `do` block statement can't start with `in`. – Ben Aug 21 '21 at 01:52
  • .... and so we should also note, that using the explicit `{ ; }` separators will eliminate all such indentation-induced problems. we should still use good indentation, for readability, but if we're a little bit off it will still work: `do { x <- xs ; let {a = b; c = d} ; y <- ys ; foo x a c y }`. – Will Ness Aug 21 '21 at 15:49

1 Answers1

2

Via Robin Zigmond's comment linking In Haskell, when do we use in with let?:

main :: IO ()
main = do
  text <- readFile "./input.txt"
  let numbers = [read line | line <- lines text]
  print $ sum numbers
  print $ product numbers
Pandu
  • 1,606
  • 1
  • 12
  • 23