user1639848
user1639848

Reputation: 572

Haskell nested where clauses

I am a beginner coder in haskell, while doing an exercise from the first chapter of this amazing book: http://book.realworldhaskell.org/read/getting-started.html I came across this issue:

-- test comment
main = interact wordCount
 where
     wordCount input = show (ls ++ " " ++ ws ++ " " ++ cs  ++ "\n")
     where
         ls = lines input
         ws = length words input
         cs = length input



wonderbox:ch01 manasapte$ runghc WC < quux.txt
WC.hs:5:9: parse error on input ‘where’

Why can I not nest my wheres ?

Upvotes: 13

Views: 10356

Answers (3)

chi
chi

Reputation: 116174

Others have already answered. I will just add some more explanation.

Simplifying a bit, the Haskell indentation rule is:

  • Some keywords start a block of things (where,let,do,case ... of).
  • Find the first word after such keywords and note its indentation. Name the column it occurs the pivot column.
  • Start a line exactly on the pivot to define a new entry in the block.
  • Start a line after the pivot to continue the entry started in the previous lines.
  • Start a line before the pivot to end the block.

Hence,

where
     wordCount input = show (ls ++ " " ++ ws ++ " " ++ cs  ++ "\n")
     where
         ls = lines input
         ws = length words input
         cs = length input

Actually means

where {
     wordCount input = show (ls ++ " " ++ ws ++ " " ++ cs  ++ "\n")
     ;
     where {     -- same column, new entry
         ls = lines input
         ;   -- same column, new entry
         ws = length words input
         ;   -- same column, new entry
         cs = length input
         }
     }

which treats the second where as a separate definition unrelated to wordCount. If we indent it more, it will work:

where {
     wordCount input = show (ls ++ " " ++ ws ++ " " ++ cs  ++ "\n")
       where {     -- after the pivot, same entry
         ls = lines input
         ;
         ws = length words input
         ;
         cs = length input
         }
     }

Upvotes: 11

user1639848
user1639848

Reputation: 572

the indentation was incorrect, here's the working version:

-- test comment
import Data.List
main = interact wordCount
    where wordCount input = unlines $ [concat $ intersperse " " (map show [ls, ws, cs])]
            where ls = length $ lines input
                  ws = length $ words input
                  cs = length input

Upvotes: 3

&#216;rjan Johansen
&#216;rjan Johansen

Reputation: 18199

Since your second where is attached to the wordCount definition, it needs to be indented more than it. (Although you will still have some other errors afterward.)

Upvotes: 15

Related Questions