Почему я не могу поместить здесь выражение функции печати? - PullRequest
2 голосов
/ 20 мая 2019

Я пытаюсь следующий код с блоком 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)

Ошибка исчезает, и программа работает хорошо, еслиоператор print удален из функции isBinary.

Почему я не могу поместить оператор печати в эту функцию?

Ответы [ 3 ]

4 голосов
/ 20 мая 2019

Ответ таков: «потому что печатает ».В частности:

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 слева.

См. this для общегоописание do обозначения.

3 голосов
/ 20 мая 2019

Вас может смущать та же самая строка печати, работающая в 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 - это язык, который поощряет использование чистых функций и применяет его с помощьюсистема типов, заставляя программиста явно помечать нечистый код.

Дальнейшее чтение: Что означает «чистый» в «чисто функциональном языке»?

2 голосов
/ 21 мая 2019

Глядя на ваш код, кажется, что использование print в isBinary не является неотъемлемой частью того, что вы хотите, чтобы функция делала, а просто оператором отладочной печати, который будет удален позже. В этом случае вы не хотите менять тип isBinary на String -> IO Bool (подробнее об этом см. Will Ness 'answer ), поскольку на самом деле вам не нужно IO, кроме для отладки. Скорее, базовые библиотеки предлагают модуль Debug.Trace , который обслуживает такую ​​ситуацию. С его помощью мы можем добавить ваш оператор отладочной печати следующим образом:

isBinary :: String -> Bool
isBinary ss = trace "In isBinary fn" $ any (\c -> ord c > 127) ss

Затем, как только вы закончите отладку, вы можете отказаться от использования trace - и это повторяет, что вы действительно должны сделать это позже. Цитирование Debug.Trace документации:

Функции для отслеживания и мониторинга выполнения.

Они могут быть полезны для исследования ошибок или проблем с производительностью. Они должны не использоваться в производственном коде.

...