Здесь есть несколько проблем. Во-первых, вам нужно включить do
после then
и else
:
if null args
then do putStr "> " ; userInput <- getLine
else do userInput <- readFile $ head args
if
в нотации do совпадает с if
везде; Вы должны поставить выражение после then
и else
, а не операторы, и вам нужно do
, чтобы превратить группу операторов в выражение. Это все еще не совсем верно, хотя; последний оператор в блоке do
должен быть выражением, но здесь у вас есть привязка. В конце концов, каждое утверждение должно иметь значение результата, а привязка - нет.
Вторая проблема, как вы заметили, заключается в том, что это вводит новую область видимости, и поэтому вы не можете получить доступ к переменным, которые привязываете извне. Это имеет смысл, если вы думаете об этом; в конце концов, вы можете связать переменную с одной стороны, а не с другой. Решение состоит в том, чтобы просто переместить связку за пределы if
:
main :: IO ()
main = do
args <- getArgs
userInput <- if null args
then do putStr "> " ; getLine
else readFile $ head args
let conclusion = userInput
Итак, действие, результат которого мы связываем с userInput
, все еще вычисляется в зависимости от результата null args
, но мы связываем переменную вне условного выражения.
Обратите внимание, что на этот раз я не добавил do
в ветку else
; это не обязательно, так как там только одно выражение. (Это все еще допустимо, но не имеет смысла использовать do
, когда в этом нет необходимости.)
Этот код все равно не будет работать, если вы не поместите что-то после строки let conclusion = userInput
(поскольку, как я уже сказал, блоки do
должны заканчиваться выражением), но, вероятно, у вас уже есть код там.
В качестве дополнительного примечания вам следует избегать использования таких функций, как head
и tail
; head
- это частичная функция (не определенная для каждого аргумента - head []
вызовет ошибку), и они обычно считаются недиоматическими. Вместо этого вы должны использовать сопоставление с шаблоном, например так:
userInput <- case args of
[] -> do putStr "> " ; getLine
fileName:_ -> readFile fileName
Это похоже на сопоставление с образцом, используемое при определении функции, но для одного значения, а не для любого количества аргументов.