Я пытаюсь использовать следующий код с блоком try-catch:
import System.Environment
import System.IO
import System.IO.Error
import Control.Exception
isBinary :: String -> Bool
isBinary ss = do
print "In isBinary fn" -- works if this line is removed.
let ans = any (\c -> ord c > 127) ss
ans
toTry :: String -> IO ()
toTry firline = do
print "In toTry fn."
let answer = isBinary firline
if not answer then do
print "Sent line not binary: "
else
print "Sent line binary"
handler :: IOError -> IO ()
handler e = putStrLn "Whoops, had some trouble!"
ss = "this is a test"
main = do
toTry ss `catch` handler
Однако я получаю следующую ошибку:
$ runghc trycatch3.hs
trycatch3.hs:9:9: error:
• Couldn't match expected type ‘Bool’ with actual type ‘IO Bool’
• In a stmt of a 'do' block: print "in isBinary fn"
In the expression:
do { print "in isBinary fn";
let ans = any (\ c -> ...) ss;
return ans }
In an equation for ‘isBinary’:
isBinary ss
= do { print "in isBinary fn";
let ans = ...;
return ans }
trycatch3.hs:10:30: error:
• Variable not in scope: ord :: Char -> Integer
• Perhaps you meant one of these:
‘or’ (imported from Prelude), ‘odd’ (imported from Prelude)
Ошибка исчезает, и программа работает хорошо, если оператор печати удаляется из функции isBinary.
Почему я не могу поместить оператор печати в эту функцию?
Я читал/слышал об этом, но не понимаю этого полностью.
Нотация do всегда дает вам какое-то значение IO (например, IO Bool в данном случае), так что это довольно просто.
Короче говоря, в Haskell нет заявления.
В чем недостаток простых утверждений? Почему их здесь так избегают?
под «утверждением» в просторечии функционального программирования подразумевается что-то, что не имеет значения и используется для его действия. принято говорить, что «функциональные» — это «ориентированные на выражения» языки, где выражения — это оценивается для их значения. так, например, в Scheme вместо явного returnутверждение, как в C, просто значение последнего оцененного выражения возвращается как общее значение выражения.





Вас может смутить одна и та же строка печати, работающая в toTry, но не в isBinary. Разница связана с декларацией:
isBinary :: String -> Bool
Это означает, что isBinary — это чистая функция (то есть без побочных эффектов), принимающая строку и возвращающая логическое значение. На самом деле, вы можете упростить его до
isBinary ss = any (\c -> ord c > 127) ss
или даже использовать безточечный стиль
isBinary = any (\c -> ord c > 127)
Тем не менее, toTry
toTry :: String -> IO ()
т.е. он принимает строку и возвращает нечистую монаду IO (может иметь побочные эффекты, такие как вывод текста на консоль).
Haskell — это язык, который поощряет использование чистых функций и навязывает их с помощью системы типов, заставляя программиста явно помечать нечистый код.
Дальнейшее чтение: Что означает «чистый» в «чистом функциональном языке»?
Ответ: «потому что типы». Конкретно:
isBinary :: String -> Bool
isBinary ss = do
....
Поскольку это блок do, возвращаемый тип isBinaryдолжен соответствует монадическому типу Monad m => m t для некоторых m и некоторых t. Здесь, поскольку print "" :: IO (), m это IO, так и должно было быть
isBinary :: String -> IO Bool
isBinary ss = do
и сейчас
print "In isBinary fn" -- works
let ans = any (\c -> ord c > 127) ss -- also works
ans -- doesn't work
ans опять же не работает из-за типов. Его тип Bool, но он должен быть IO Bool -- первым, потому что этот do блок принадлежит IO монаде из-за print; и во-вторых, из-за возвращаемого типа функции в целом.
Вместо этого используйте
return ans
и теперь это будет работать, потому что return вводит значение в монадический контекст, и, будучи последним значением блока do, оно становится значением, созданным блоком do в целом (если return val появляется в середине, он просто передает val на следующий шаг в комбинированном расчете).
Функция toTry должна быть дополнена, чтобы использовать новое определение:
toTry :: String -> IO ()
toTry firline = do
print "In toTry fn."
-- let answer = isBinary firline -- incorrect, now!
answer <- isBinary firline -- isBinary ... :: IO Bool
if not answer then do -- answer :: Bool
print "Sent line not binary: "
else
print "Sent line binary"
m a справа от <-, a слева.
См. это для общего описания нотации do.
Отличный ответ, я бы также добавил краткое объяснение return в хаскеле по сравнению с его значением в других языках, так как это вызывает путаницу у многих учащихся.
@m-aroosi спасибо за редактирование! Эту ошибку я часто замечал в чужих постах, а тут сделал сам. Ирония!
Глядя на ваш код, кажется, что использование вами print в isBinary не является неотъемлемой частью того, что вы хотите, чтобы функция делала, а просто оператор отладочной печати, который будет удален позже. В этом случае вы не хотите менять тип isBinary на String -> IO Bool (подробнее об этом см. Ответ Уилла Несса), так как на самом деле вам не нужно IO, кроме как для отладки. Скорее, основные библиотеки предлагают модуль Debug.Trace, который подходит для таких ситуаций. С его помощью мы можем добавить оператор отладочной печати следующим образом:
isBinary :: String -> Bool
isBinary ss = trace "In isBinary fn" $ any (\c -> ord c > 127) ss
Затем, когда вы закончите отладку, вы можете отказаться от использования trace — и стоит повторить, что вам действительно следует сделать это позже. Цитирование документации Debug.Trace:
Functions for tracing and monitoring execution.
These can be useful for investigating bugs or performance problems. They should not be used in production code.
Вы понимаете, что означает «чистый» в утверждении «Haskell — это чистый функциональный язык»?