Haskell имеет эквивалент with
для файлов, он называется withFile
.Это:
with open("file1", "w") as f:
with open("file2", "r") as g:
k = g.readline()
f.write(k)
эквивалентно:
withFile "file1" WriteMode $ \f ->
withFile "file2" ReadMode $ \g ->
do k <- hGetLine g
hPutStr f k
Теперь withFile
может выглядеть как нечто монадическое.Его тип:
withFile :: FilePath -> IOMode -> (Handle -> IO r) -> IO r
правая сторона выглядит как (a -> m b) -> m b
.
Еще одно сходство: в Python вы можете пропустить as
, а в Haskell вы можете использовать >>
вместо>>=
(или блок do
без стрелки <-
).
Поэтому я отвечу на этот вопрос: withFile
монаден?
Можно подумать, что он можетбыть написано так:
do f <- withFile "file1" WriteMode
g <- withFile "file2" ReadMode
k <- hGetLine g
hPutStr f k
Но это не проверка типа.И это не может.
Это потому, что в Haskell монада ввода-вывода является последовательной : если вы пишете
do x <- a
y <- b
c
после выполнения a
, выполняется b
а затем c
.Не существует «возврата» для очистки a
в конце или что-то в этом роде.withFile
, с другой стороны, должен закрывать дескриптор после выполнения блока.
Существует еще одна монада, называемая продолжением, которая позволяет делать такие вещи.Однако теперь у вас есть две монады, IO и продолжения, и использование эффектов двух монад одновременно требует использования монадных преобразователей.
import System.IO
import Control.Monad.Cont
k :: ContT r IO ()
k = do f <- ContT $ withFile "file1" WriteMode
g <- ContT $ withFile "file2" ReadMode
lift $ hGetLine g >>= hPutStr f
main = runContT k return
Это ужасно.Таким образом, ответ: несколько, но для этого нужно разобраться со множеством тонкостей, которые делают проблему довольно непрозрачной.
Python's with
может имитировать только ограниченный бит того, что могут делать монады - добавить код ввода и завершения,Я не думаю, что вы можете смоделировать, например,
do x <- [2,3,4]
y <- [0,1]
return (x+y)
, используя with
(это может быть возможно с некоторыми грязными взломами).Вместо этого используйте для:
for x in [2,3,4]:
for y in [0,1]:
print x+y
И для этого есть функция Haskell - forM
:
forM [2,3,4] $ \x ->
forM [0,1] $ \y ->
print (x+y)
Я рекомендовал прочитать о yield
, который больше похож на монады, чем with
: http://www.valuedlessons.com/2008/01/monads-in-python-with-nice-syntax.html
Смежный вопрос: если у нас есть монады, нужны ли нам исключения?
В основном нет, вместо функции, которая выдает A или возвращаетB вы можете сделать функцию, которая возвращает Either A B
.Монада для Either A
будет тогда вести себя как исключения - если одна строка кода выдаст ошибку, весь блок будет.
Однако это будет означать, что деление будет иметь тип Integer -> Integer -> Either Error Integer
и т. Д., чтобы поймать деление на ноль.Вы должны будете обнаруживать ошибки (явное совпадение с образцом или использовать связывание) в любом коде, который использует деление или имеет даже малейшую возможность ошибиться.Haskell использует исключения, чтобы избежать этого.