GHC.IO
| Авторские права | (c) Университет Глазго 1994-2023 |
|---|---|
| Лицензия | см. libraries/base/LICENSE |
| Поддержка | ghc-devs@haskell.org |
| Стабильность | внутренняя |
| Переносимость | непереносимая (расширения GHC) |
| Безопасный Haskell | Нет |
| Язык | Haskell2010 |
Описание
Определения для монады IO и её друзей.
API данного модуля нестабилен и не предназначен для использования широкой публикой. Если вам абсолютно необходимо от него зависеть, убедитесь, что вы используете жёсткий верхний предел, например, base < 4.X, а не base < 5, поскольку интерфейс может быстро изменяться без предупреждения.
Значение типа IO a — это вычисление, которое при выполнении выполняет некоторое ввод-вывод, прежде чем вернуть значение типа a.
Существует только один способ "выполнить" действие ввода-вывода: связать его с Main.main в вашей программе. При выполнении программы ввод-вывод будет выполнен. Невозможно выполнить ввод-вывод из произвольной функции, если эта функция не находится в монаде IO и не вызывается в какой-то момент, напрямую или косвенно, из Main.main.
IO — это монада, поэтому действия IO могут быть объединены с помощью синтаксиса do или операций >> и >>= из класса Monad.
Экземпляры
| Alternative IO Источник |
Возвращает результат первого действия, которое не вызывает исключение. С версии: base-4.9.0.0 |
| Applicative IO Источник | С версии: base-2.1 |
| Functor IO Источник | С версии: base-2.1 |
| Monad IO Источник | С версии: base-2.1 |
| MonadPlus IO Источник |
Возвращает результат первого действия, которое не вызывает исключение. С версии: base-4.9.0.0 |
| MonadFail IO Источник | С версии: base-4.9.0.0 |
Определено в GHC.Internal.Control.Monad.Fail | |
| MonadFix IO Источник | С версии: base-2.1 |
Определено в GHC.Internal.Control.Monad.Fix | |
| MonadIO IO Source | С момента: base-4.9.0.0 |
Определено в GHC.Internal.Control.Monad.IO.Class | |
| GHCiSandboxIO IO Source | С момента: base-4.4.0.0 |
Определено в GHC.Internal.GHCi МетодыghciStepIO :: IO a -> IO a Source | |
| Quasi IO Source | |
Defined in GHC.Internal.TH.Syntax Краткое описание методовqNewName :: String -> IO Name Source qReport :: Bool -> String -> IO () Source qRecover :: IO a -> IO a -> IO a Source qLookupName :: Bool -> String -> IO (Maybe Name) Source qReify :: Name -> IO Info Source qReifyFixity :: Name -> IO (Maybe Fixity) Source qReifyType :: Name -> IO Type Source qReifyInstances :: Name -> [Type] -> IO [Dec] Source qReifyRoles :: Name -> IO [Role] Source qReifyAnnotations :: Data a => AnnLookup -> IO [a] Source qReifyModule :: Module -> IO ModuleInfo Source qReifyConStrictness :: Name -> IO [DecidedStrictness] Source qGetPackageRoot :: IO FilePath Source qAddDependentFile :: FilePath -> IO () Source qAddTempFile :: String -> IO FilePath Source qAddTopDecls :: [Dec] -> IO () Source qAddForeignFilePath :: ForeignSrcLang -> String -> IO () Source qAddModFinalizer :: Q () -> IO () Source qAddCorePlugin :: String -> IO () Source |
unIO :: IO a -> Состояние RealWorld -> (# Состояние RealWorld, a #) Исходный код
liftIO :: IO a -> Состояние RealWorld -> STret RealWorld a Исходный код
mplusIO :: IO a -> IO a -> IO a Исходный код
unsafePerformIO :: IO a -> a Исходный код
Это "задний ход" в IO монаде, позволяющий IO вычисления выполняться в любое время. Для того, чтобы это было безопасно, IO вычисление должно быть лишено побочных эффектов и независимо от своей среды.
Если вычисление I/O, заключённое в unsafePerformIO, имеет побочные эффекты, то относительный порядок этих побочных эффектов (по отношению к основному стволу I/O или другим вызовам unsafePerformIO) неопределён. Кроме того, при использовании unsafePerformIO для создания побочных эффектов, следует принять следующие меры предосторожности, чтобы гарантировать, что побочные эффекты выполняются столько раз, сколько вы ожидаете. Обратите внимание, что эти меры предосторожности необходимы для GHC, но могут быть недостаточными, и другие компиляторы могут потребовать других мер предосторожности:
- Используйте
{-# NOINLINE foo #-}в качестве прагмы для любой функцииfoo, которая вызываетunsafePerformIO. Если вызов встроен, ввод/вывод может выполняться более одного раза. - Используйте флаг компилятора
-fno-cse, чтобы предотвратить выполнение оптимизации общих подвыражений для модуля, которая может объединять два побочных эффекта, которые должны быть отдельными. Хорошим примером является использование нескольких глобальных переменных (например,testв примере ниже). - Убедитесь, что либо вы отключили плавание let (
-fno-full-laziness), либо вызовunsafePerformIOне может быть вынесен за пределы лямбды. Например, если вы напишете:f x = unsafePerformIO (newIORef []), вы можете получить только одну общую ячейку для ссылок между всеми вызовамиf. Лучше будетf x = unsafePerformIO (newIORef [x]), так как теперь он не может быть вынесен за пределы лямбды.
Менее известно, что unsafePerformIO не является типобезопасным. Например:
test :: IORef [a]
test = unsafePerformIO $ newIORef []
main = do
writeIORef test [42]
bang <- readIORef test
print (bang :: [Char])
Эта программа завершится ошибкой. Эта проблема с полиморфными ссылками хорошо известна в сообществе ML и не возникает при обычном монадическом использовании ссылок. Нет простого способа сделать это невозможным после использования unsafePerformIO. Действительно, возможно написать coerce :: a -> b с помощью unsafePerformIO. Поэтому будьте осторожны!
ПРЕДУПРЕЖДЕНИЕ: Если вы ищете «способ получить String из „IO String“, то unsafePerformIO не подходит. Ознакомьтесь с синтаксисом `do-notation` и элементом <- перед продолжением.
unsafeInterleaveIO :: IO a -> IO a Source
unsafeInterleaveIO позволяет откладывать вычисление IO лениво. При передаче значения типа IO a, IO будет выполнено только при запросе значения a. Это используется для реализации ленивого чтения файлов, см. hGetContents.
unsafeDupablePerformIO :: IO a -> a Source
Этот вариант unsafePerformIO более эффективен, потому что он опускает проверку, что операция IO выполняется только одним потоком. Следовательно, когда вы используете unsafeDupablePerformIO, существует возможность, что операция IO может выполняться несколько раз (на многопроцессорной системе), и поэтому вы должны убедиться, что она дает одни и те же результаты каждый раз. Может даже случиться так, что одна из дублированных операций IO будет выполнена частично, а затем прервана посредине без возникновения исключения. Поэтому функции, такие как bracket, нельзя безопасно использовать внутри unsafeDupablePerformIO.
С версии: base-4.4.0.0
unsafeDupableInterleaveIO :: IO a -> IO a Source
unsafeDupableInterleaveIO позволяет лениво откладывать вычисление IO. При передаче значения типа IO a, IO будет выполнено только при запросе значения a.
Вычисление может быть выполнено несколько раз разными потоками, возможно, одновременно. Чтобы убедиться, что вычисление выполняется только один раз, используйте unsafeInterleaveIO.
noDuplicate :: IO () Source
Гарантирует, что суспензии, оцениваемые текущим потоком, уникальны; то есть текущий поток не оценивает ничего, что также оценивается другим потоком, который также выполнил noDuplicate.
Эта операция используется в определении unsafePerformIO, чтобы предотвратить выполнение операции IO несколько раз, что обычно нежелательно.
stToIO :: ST RealWorld a -> IO a Source
Встраивание строгого потока состояния в действие IO. Параметр RealWorld указывает, что внутреннее состояние, используемое вычислением ST, является специальным, предоставляемым монадой IO, и, следовательно, отличается от состояний, используемых вызовами runST.
ioToST :: IO a -> ST RealWorld a Source
Преобразование действия IO в действие ST. Тип результата ограничен использованием потока состояния RealWorld, и поэтому результат нельзя передать runST.
unsafeIOToST :: IO a -> ST s a Source
Преобразование действия IO в действие ST. Это основано на том, что IO и ST имеют одинаковое представление с учётом ограничения на тип параметра потока состояния.
unsafeSTToIO :: ST s a -> IO a Source
Преобразование действия ST в действие IO. Это основано на том, что IO и ST имеют одинаковое представление с учётом ограничения на тип параметра потока состояния.
Пример, демонстрирующий, почему это небезопасно, см. https://mail.haskell.org/pipermail/haskell-cafe/2009-April/060719.html
Имена файлов и каталогов являются значениями типа String, чёткое значение которого зависит от операционной системы. Файлы могут открываться, что даёт дескриптор, который затем используется для работы с содержимым файла.
Аргументы
| :: Exception e | |
| => IO a | Вычисляемое выражение |
| -> (e -> IO a) | Обработчик для вызова, если возникает исключение |
| -> IO a |
Это самая простая функция обработки исключений. Она принимает один аргумент, выполняет его, и если возникает исключение, вызывается «обработчик» со значением исключения в качестве аргумента. В противном случае возвращается результат как обычно. Например:
catch (readFile f)
(\e -> do let err = show (e :: IOException)
hPutStr stderr ("Warning: Couldn't open " ++ f ++ ": " ++ err)
return "")
Обратите внимание, что нам нужно указать сигнатуру типа для e, иначе программа не пройдёт типизацию, так как тип неясен. Хотя можно перехватывать исключения любого типа, см. раздел «Перехват всех исключений» (в Control.Exception) для объяснения проблем, связанных с этим.
Для перехвата исключений в чистых (не-IO) выражениях см. функцию evaluate.
Обратите внимание, что из-за неопределённого порядка вычисления Haskell выражение может выбросить одно из нескольких возможных исключений: рассмотрим выражение (error "urk") + (1 `div` 0). Выбрасывает ли выражение ErrorCall "urk" или DivideByZero?
Ответ: «может выбросить либо то, либо другое»; выбор недетерминирован. Если вы перехватываете любой тип исключения, то вы можете перехватить либо одно, либо другое. Если вы вызываете catch с типом IO Int -> (ArithException -> IO Int) -> IO Int, обработчик может быть запущен с DivideByZero в качестве аргумента или исключение ErrorCall "urk" может быть распространено дальше. Если вы снова вызовете его, вы можете получить обратное поведение. Это нормально, потому что catch — это вычисление в монаде IO.
catchException :: Exception e => IO a -> (e -> IO a) -> IO a Source
Перехват исключения в монаде IO.
Обратите внимание, что эта функция строгая в отношении действия. То есть, catchException undefined b == _|_. См. для получения подробной информации.
catchAny :: IO a -> (forall e. (HasExceptionContext, Exception e) => e -> IO a) -> IO a Source
Перехватывает любой исключение Exception типа в монаде IO.
Обратите внимание, что эта функция строгая по отношению к действию. То есть, catchAny undefined b == _|_. Подробности см. в документации.
При повторном возбуждении исключения необходимо повторно использовать предоставленный ExceptionContext.
throwIO :: (HasCallStack, Exception e) => e -> IO a Source
Вариант throw, который может использоваться только в рамках монады IO.
Несмотря на то, что throwIO имеет тип, являющийся экземпляром типа throw, эти две функции отличаются:
throw e `seq` () ===> throw e throwIO e `seq` () ===> ()
В первом примере будет вызвано исключение e, а во втором — нет. Фактически, throwIO вызывает исключение только при использовании в монаде IO.
Следует предпочесть вариант throwIO вместо throw для возбуждения исключения в рамках монады IO, так как он гарантирует порядок выполнения по отношению к другим операциям, в то время как throw этого не делает. Мы говорим, что throwIO возбуждает *точные* исключения, а throw, error и т.д. — *неточные* исключения. Например
throw e + error "boom" ===> error "boom" throw e + error "boom" ===> throw e
оба являются допустимыми вычислениями, и компилятор может выбрать любой (цикл, даже), в то время как
throwIO e >> error "boom" ===> throwIO e
всегда возбудит исключение e при выполнении.
См. также страницу вики-справочника GHC по точным исключениям для более технического введения в то, как GHC оптимизирует работу с точными и неточными исключениями.
mask :: ((forall a. IO a -> IO a) -> IO b) -> IO b Source
Выполняет вычисление IO с замаскированными асинхронными исключениями. То есть, любая нить, которая пытается возбудить исключение в текущей нити с помощью throwTo, будет заблокирована до повторного размаскирования асинхронных исключений.
Передаваемый аргумент функции mask — функция, которая принимает другой аргумент-функцию, которую можно использовать для восстановления состояния маскировки в контексте замаскированного вычисления. Например, распространенный способ использования mask — защита получения ресурса:
mask $ \restore -> do
x <- acquire
restore (do_something_with x) `onException` release
release
Этот код гарантирует, что acquire сопоставлено с release, маскируя асинхронные исключения для критических частей. (Вместо написания этого кода, лучше использовать bracket, которая абстрагирует общий шаблон).
Обратите внимание, что действие restore, переданное в аргумент функции mask, не обязательно размаскирует асинхронные исключения, оно лишь восстанавливает состояние маскировки до состояния окружающего контекста. Таким образом, если асинхронные исключения уже замаскированы, то mask нельзя использовать для размаскирования исключений. Это делается для того, чтобы при вызове функции библиотеки с замаскированными исключениями можно было быть уверенным, что вызов библиотеки не сможет снова размаскировать исключения. Если вы пишете код библиотеки и вам нужно использовать асинхронные исключения, единственный способ — создать новую нить; см. forkIOWithUnmask.
Асинхронные исключения все еще могут быть получены во время замаскированного состояния, если замаскированная нить блокируется определёнными способами; см. Control.Exception.
Потоки, созданные с помощью forkIO, наследуют состояние MaskingState от родителя; то есть, чтобы запустить поток в состоянии MaskedInterruptible, используйте mask_ $ forkIO .... Это особенно полезно, если вам нужно установить обработчик исключений в виртуальном потоке до получения любых асинхронных исключений. Чтобы создать новый поток в незамаскированном состоянии, используйте forkIOWithUnmask.
Подобно mask, но не передает действие restore в аргумент.
uninterruptibleMask :: ((forall a. IO a -> IO a) -> IO b) -> IO b Source
Подобно mask, но замаскированное вычисление не прерывается (см. Control.Exception). ЭТОГО СЛЕДУЕТ ИСПОЛЬЗОВАТЬ С ОСОБОЙ ОСТОРОЖНОСТЬЮ, потому что если нить, выполняющаяся в uninterruptibleMask, блокируется по какой-либо причине, то нить (и, возможно, вся программа, если это главная нить) станет неотзывчивой и неуничтожимой. Эта функция необходима только если вам нужно замаскировать исключения вокруг прерываемой операции, и вы можете гарантировать, что прерываемая операция будет заблокирована только на короткий промежуток времени.
uninterruptibleMask_ :: IO a -> IO a Source
Подобно uninterruptibleMask, но не передает действие restore в аргумент.
data MaskingState Source
Описывает поведение нити при получении асинхронного исключения.
Конструкторы
| Unmasked | асинхронные исключения размаскированы (нормальное состояние) |
| MaskedInterruptible | состояние во время |
| MaskedUninterruptible | состояние во время |
Примеры реализации
| Show MaskingState Исходный код | С момента: base-4.3.0.0 |
Определено в GHC.Internal.IO МетодыshowsPrec :: Int -> MaskingState -> ShowS Исходный код show :: MaskingState -> String Исходный код showList :: [MaskingState] -> ShowS Исходный код | |
| Eq MaskingState Исходный код | С момента: base-4.3.0.0 |
Определено в GHC.Internal.IO Методы(==) :: MaskingState -> MaskingState -> Bool Исходный код (/=) :: MaskingState -> MaskingState -> Bool Исходный код | |
getMaskingState :: IO MaskingState Исходный код
Возвращает MaskingState для текущей потока.
unsafeUnmask :: IO a -> IO a Исходный код
interruptible :: IO a -> IO a Исходный код
Разрешает возникновение асинхронных исключений даже внутри mask, делая операцию прерывимой (см. обсуждение «Прерывимых операций» в Exception).
При вызове вне mask или внутри uninterruptibleMask, эта функция не оказывает никакого влияния.
С момента: base-4.9.0.0
onException :: IO a -> IO b -> IO a Исходный код
Аргументы
| :: IO a | вычисление, которое выполняется первым («получение ресурса») |
| -> (a -> IO b) | вычисление, которое выполняется последним («освобождение ресурса») |
| -> (a -> IO c) | вычисление, которое выполняется между ними |
| -> IO c |
Аргументы
| :: IO a | вычисление, которое выполняется первым |
| -> IO b | вычисление, которое выполняется после (даже если было вызвано исключение) |
| -> IO a |
evaluate :: a -> IO a Исходный код
Вычисляет аргумент до слабой нормальной формы головной части.
evaluate обычно используется для обнаружения любых исключений, которые может содержать ленивое значение, и, возможно, их обработки.
evaluate вычисляет только до слабой нормальной формы головной части. Если требуется более глубокое вычисление, функция force из Control.DeepSeq может быть полезной:
evaluate $ force x
Существует тонкое различие между evaluate x и return $! x, аналогичное различию между throwIO и throw. Если ленивое значение x вызывает исключение, return $! x не сможет вернуть действие IO и вызовет исключение вместо этого. evaluate x, с другой стороны, всегда генерирует действие IO; это действие вызовет исключение при выполнении тогда и только тогда, когда x вызовет исключение при вычислении.
Практическим следствием этого различия является то, что из-за семантики неточных исключений
(return $! error "foo") >> error "bar"
может вызвать либо "foo", либо "bar", в зависимости от оптимизаций, выполненных компилятором. С другой стороны,
evaluate (error "foo") >> error "bar"
гарантированно вызывает "foo".
Правило для использования: используйте evaluate, чтобы принудительно вычислить или обработать исключения в ленивых значениях. Если же вы принуждаете вычисление ленивого значения только для повышения эффективности и не заботитесь об исключениях, вы можете использовать return $! x.
mkUserError :: [Char] -> SomeException Исходный код
© The University of Glasgow and others
Licensed under a BSD-style license (see top of the page).
https://downloads.haskell.org/~ghc/9.12.1/docs/libraries/base-4.21.0.0-8e62/GHC-IO.html