显然,以下功能是不可能的,因为不可能永久解开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不可能的原因,但我不知道如何。我还可以自己创建不可包装的类型吗?
答案 0 :(得分:11)
Just
和Nothing
是Maybe a
类型的数据构造函数。 IO
没有要说的数据构造函数(在GHC中它实际上有构造函数,但它们实际上是GHC的实现细节,其他实现可能对IO
的定义不同)。
unwrapIO (IO str) = str
与unwrapMaybe (Maybe str) = str
毫无意义。 IO
和Maybe
不是数据构造函数,因此无法对其进行模式匹配。
答案 1 :(得分:9)
这是因为未导出IO
的数据构造函数。我的意思是,您可以认为它没有导出。
您可以使用相同的策略来防止自己的类型被解包。
module Test (Test, test) where
data Test a = MkTest a
test :: a -> Test a
test = MkTest
您可以使用Test
创建test
的值,但是由于不导出MkTest
,因此无法使用模式匹配来取消包装。
答案 2 :(得分:1)
我认为,尽管现有的答案大部分都是正确的,但还有一个更深层次的原因导致无法解开IO
。 Conceptually,type IO a = RealWorld -> (a, RealWorld)
。 IO
是一种函数类型(在实际的实现中,它隐藏在新包装器或等效机器的后面)。
那么,您将如何解开函数?很简单,您只需调用它即可。但是,您将如何获取RealWorld
的实例?那是更真实的原语:您无法构造RealWorld
,只有一个。
当然,单子实例可以仅将RealWorld
作为状态传递,类似于StateT
,最后,唯一构造的实例是在程序启动时传递的,然后向下传递在IO
之间。
再次回到现实,这(再次)是一个谎言。实际上,您可以拥有RealWorld
的实例,并且可以“提前”调用IO a
动作。有一个功能可以完全满足您的要求。尽管它出于某种原因不安全,但仍被称为System.IO.Unsafe.unsafePerformIO :: IO a -> a
。