Haskell IO отступ
Я попытался переписать ту программу, которая работает:
nameIOite :: IO ()
nameIOite = do
putStrLn "What's your name ?"
name <- getLine
if name `elem` ["Simon","John","Phil"]
--if name == "Simon" || name == "John" || name == "Phil" also works but is ugly.
then putStrLn "I think Haskell is a great programming language."
else if name == "Koen"
then putStrLn "I think debugging Haskell is fun."
else putStrLn "I don't know your name."
Это делается с использованием if/then/else (поэтому суффикс ite в nameIOite
)
Тогда я попробовал использовать охрану:
nameIOg :: IO ()
nameIOg = do
putStrLn "What's your name ?"
name <- getLine
let answer
| name `elem` ["Simon","John","Phil"] = "I think Haskell is a great programming language."
| name == "Koen" = "I think debugging Haskell is fun."
| otherwise = "I don't know your name."
putStrLn answer
Это не сработало:
test.hs:6:9: error:
parse error (possibly incorrect indentation or mismatched brackets)
|
6 | | name `elem` ["Simon","John","Phil"] = "I think Haskell is a great programming language."
| ^
Failed, no modules loaded.
После некоторых экспериментов решение оказалось сделать отступы для охранников еще раз (что мне не совсем понятно):
nameIOg :: IO ()
nameIOg = do
putStrLn "What's your name ?"
name <- getLine
let answer
| name `elem` ["Simon","John","Phil"] = "I think Haskell is a great programming language."
| name == "Koen" = "I think debugging Haskell is fun."
| otherwise = "I don't know your name."
putStrLn answer
Ok, one module loaded.
Откуда берется это двойное отступление и есть ли способ написать это более изящно?
(Кстати, я наткнулся на это, просматривая мои файлы wikibook.hs.)
Источник примера: там
Решения: есть
2 ответа
let
позволяет несколько определений, как в
main = do
doSomething
let x = 1
y = 2
z = 3
print (x+y+z)
Обратите внимание на отступ. y = 2
не анализируется, чтобы продолжить определение x = 1
поскольку он начинается в том же столбце.
Если вы хотите, чтобы новая строка анализировалась так, как если бы она продолжала предыдущую строку, вы должны сделать отступ больше. Например
main = do
doSomething
let x | someCondition = 1
| otherwise = 0 -- more indented
y = 2
z = 3
print (x+y+z)
или, используя другую строку
main = do
doSomething
let x
| someCondition = 1 -- more indented
| otherwise = 0 -- more indented
y = 2
z = 3
print (x+y+z)
Правила отступов на первый взгляд могут показаться загадочными, но на самом деле они довольно просты.
Я думаю, что ваш текущий код настолько элегантен, насколько это возможно - он мне подходит.
Если вы хотите больше альтернатив, вы можете использовать if then else
, даже если большинство Haskellers предпочли бы охранников. (Лично у меня нет реальных предпочтений)
main = do
doSomething
let x = if condition then 1
else if someOtherCondition then 0
else -1
y = 2
z = 3
print (x+y+z)
Вы также можете использовать другую строку, например (я бы предпочел это)
main = do
doSomething
let x =
if condition then 1
else if someOtherCondition then 0
else -1
y = 2
z = 3
print (x+y+z)
или даже
main = do
doSomething
let x =
if condition
then 1
else if someOtherCondition
then 0
else -1
y = 2
z = 3
print (x+y+z)
Я не утверждаю, что стиль намного лучше, чем другой.
Другим вариантом является встроенное сопоставление шаблонов для типов сумм. Это хорошо, если у вас есть небольшой кусок кода, и вы не хотите использовать несколько строк.
z <- maybeDoSomething :: IO (Maybe Int)
let x = case z of { Nothing -> 0; Just v -> v }
Это также может сократить пространство, необходимое для сопоставления с образцом в анонимных функциях. Это:
(\x -> case t of
Nothing -> 0
Just v -> v
)
Может быть изменено на это:
(\x -> case t of { Nothing -> 0; Just v -> v })
Вы также можете избежать if-then-else
,
t <- didSomethingSucceed :: IO Bool
let x = case t of { True -> 1; False -> 0 }
Я бы использовал это, только если строки могут быть короткими, и у вас есть небольшое количество шаблонов для сравнения, иначе это может быть трудно для чтения.