Haskell编译器如何“知道”无法解包IO?

时间:2018-11-27 00:23:05

标签: haskell pattern-matching type-constructor

显然,以下功能是不可能的,因为不可能永久解开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不可能的原因,但我不知道如何。我还可以自己创建不可包装的类型吗?

3 个答案:

答案 0 :(得分:11)

JustNothingMaybe a类型的数据构造函数。 IO没有要说的数据构造函数(在GHC中它实际上有构造函数,但它们实际上是GHC的实现细节,其他实现可能对IO的定义不同)。

unwrapIO (IO str) = strunwrapMaybe (Maybe str) = str毫无意义。 IOMaybe不是数据构造函数,因此无法对其进行模式匹配。

答案 1 :(得分:9)

这是因为未导出IO的数据构造函数。我的意思是,您可以认为它没有导出。

您可以使用相同的策略来防止自己的类型被解包。

module Test (Test, test) where

data Test a = MkTest a

test :: a -> Test a
test = MkTest

您可以使用Test创建test的值,但是由于不导出MkTest,因此无法使用模式匹配来取消包装。

答案 2 :(得分:1)

我认为,尽管现有的答案大部分都是正确的,但还有一个更深层次的原因导致无法解开IOConceptuallytype IO a = RealWorld -> (a, RealWorld)IO是一种函数类型(在实际的实现中,它隐藏在新包装器或等效机器的后面)。

那么,您将如何解开函数?很简单,您只需调用它即可。但是,您将如何获取RealWorld的实例?那是更真实的原语:您无法构造RealWorld,只有一个。

当然,单子实例可以仅将RealWorld作为状态传递,类似于StateT,最后,唯一构造的实例是在程序启动时传递的,然后向下传递在IO之间。

再次回到现实,这(再次)是一个谎言。实际上,您可以拥有RealWorld的实例,并且可以“提前”调用IO a动作。有一个功能可以完全满足您的要求。尽管它出于某种原因不安全,但仍被称为System.IO.Unsafe.unsafePerformIO :: IO a -> a