Как компилятор 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 хотя причина в небезопасной упаковке.

Другие вопросы по тегам