Haskell “where” indentation: why must it be indented past identifier?

六月ゝ 毕业季﹏ 提交于 2019-11-26 20:10:11

问题


This code:

import Data.Char (digitToInt)

myInt :: String -> Int
myInt [] = error "bad input: empty string"
myInt (x:xs)
  | x == '-'  = -1 * myInt xs
  | otherwise = foldl convert 0 (x:xs)
  where convert acc x
        | x `elem` ['0'..'9'] = 10 * acc + digitToInt x
        | otherwise           = error ("bad input: not an int - " ++ [x])

Fails:

Prelude> :l safeListFs.hs
[1 of 1] Compiling Main ( safeListFs.hs, interpreted )

safeListFs.hs:9:8: parse error (possibly incorrect indentation)
Failed, modules loaded: none.

But this version:

import Data.Char (digitToInt)

myInt :: String -> Int
myInt [] = error "bad input: empty string"
myInt (x:xs)
  | x == '-'  = -1 * myInt xs
  | otherwise = foldl convert 0 (x:xs)
  where convert acc x
          | x `elem` ['0'..'9'] = 10 * acc + digitToInt x
          | otherwise           = error ("bad input: not an int - " ++ [x])

is ok:

Prelude> :l safeListFs.hs
[1 of 1] Compiling Main ( safeListFs.hs, interpreted )
Ok, modules loaded: Main.

I can't figure out why those two last indents matter.


回答1:


Basically, Haskell notes the column where the first non-space character after where appears (in this case, the c of convert) and treats following lines beginning in that column as new definitions inside the where.

A line that continues the definition of the previous line (such as your | guards) must be indented to the right of the first non-space character (c in your code).

A line indented to the left of c would be outside the where (for example, the start of your next top-level function).

It's the column of the first character following where that is crucial, even if it's on a new line:

  where
    convert acc x
      | ...
    anotherFunction x y

    ^ 



回答2:


A nested context must be further indented than the enclosing context (n>m). If not, L fails, and the compiler should indicate a layout error.

From http://www.haskell.org/onlinereport/syntax-iso.html.

This would also fail:

import Data.Char (digitToInt)

myInt :: String -> Int
myInt [] = error "bad input: empty string"
myInt (x:xs)
| x == '-'  = -1 * myInt xs
| otherwise = foldl convert 0 (x:xs)
where convert acc x
        | x `elem` ['0'..'9'] = 10 * acc + digitToInt x
        | otherwise           = error ("bad input: not an int - " ++ [x])

Uh, I'm bad at explaining things. There's a new context after where keyword, because you can specify more than one function in there -- remember that your program begins with implicit module Main where, so I think it's logical to require function body to be indented, just like on the module level (compiler expects another identifier on columns M and N, and declaration bodies to be further indented).

fun = ...
^ where fun' = ...
M       ^
        N
        fun'' = ...
fun2 = ...



回答3:


Because you should always indent function definitions. (In your case, all things started at same column in where are considered "same-level" definition).



来源:https://stackoverflow.com/questions/2223468/haskell-where-indentation-why-must-it-be-indented-past-identifier

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!