Как компилятор Haskell "знает", что IO не может быть развернут?
Очевидно, что следующая функция невозможна, поскольку невозможно постоянно развернуть значение IO (игнорируя unsafePerformIO или подобное):
unwrapIO :: IO String -> String
unwrapIO (IO str) = str
Однако возможны похожие функции, такие как следующие:
unwrapJust :: Maybe String -> String
unwrapJust (Just str) = str
unwrapJust Nothing = "ignore this plz"
Я полностью понимаю причину, по которой № 2 возможен, а № 1 нет, но я не понимаю, как. Могу ли я также сделать свои собственные типы, которые не могут быть распакованы?
2 ответа
Just
а также Nothing
являются конструкторами данных для типа Maybe a
, IO
не имеет конструкторов данных, о которых можно говорить (в GHC на самом деле есть конструкторы, но они на самом деле представляют детали реализации GHC, и другие реализации могут определять IO
по-другому).
unwrapIO (IO str) = str
не имеет смысла таким же образом unwrapMaybe (Maybe str) = str
не имеет смысла. IO
а также Maybe
не являются конструкторами данных, поэтому вы не можете сопоставить их с шаблоном.
Это потому, что конструктор данных IO
не экспортируется. Я имею в виду, вы можете думать, что это не экспортируется.
Вы можете предотвратить развертывание вашего собственного типа, используя ту же стратегию.
module Test (Test, test) where
data Test a = MkTest a
test :: a -> Test a
test = MkTest
Вы можете создать значение Test
с помощью test
, но вы не можете развернуть его с помощью сопоставления с образцом, потому что MkTest
не экспортируется.
Я считаю, что, хотя существующие ответы в основном верны, есть более глубокая причина, почему IO
не может быть развернут. Концептуально type IO a = RealWorld -> (a, RealWorld)
, IO
тип функции (в реальных реализациях, скрытых за оболочкой нового типа или эквивалентным механизмом).
Итак, как бы вы развернули функцию? Полегче, просто позвони! Но как вы собираетесь получить экземпляр RealWorld
? Это истинный примитив: вы не можете построить RealWorld
Есть только один.
Монадный экземпляр конечно может просто пройти RealWorld
как государство, вроде как StateT
и, в конце концов, единственный когда-либо созданный экземпляр находится при запуске программы, затем он передается между IO
s.
Возвращаясь к реальности снова, это (снова) ложь. Вы на самом деле можете получить экземпляр RealWorld
и вы можете позвонить IO a
акция "раньше времени". Есть функция, которая делает именно то, что вы просили. Это называется System.IO.Unsafe.unsafePerformIO :: IO a -> a
хотя причина в небезопасной упаковке.