为什么 Haskell 管道 "use () to close unused inputs and X (the uninhabited type) to close unused outputs"?

Why does Haskell Pipes "use () to close unused inputs and X (the uninhabited type) to close unused outputs"?

Pipes Tutorial中说:

The concrete type synonyms use () to close unused inputs and X (the uninhabited type) to close unused outputs:

我想了解为什么 ()X 是这样使用的。为什么不对输入和输出都使用 X()

管道中的

X 在 Haskell 生态系统的其余部分通常拼写为 Void,所以让我们假装 X = Void。它是这样定义的:

data Void

它有一个 "eliminator"

absurd :: Void -> a
absurd x = case x of {}

如果您有 Void 类型的内容(并强制执行),那么 出了问题 。您的程序产生了错误,或者陷入了无限循环。

制作管道 生产 类型的东西 Void 禁止它生产任何东西(合法)。让它产生 () 类型的东西允许它产生东西,但是不携带任何信息的东西。它们基本上是时钟滴答声。

在输入端,消耗类型 Void 的管道可以等待输入,但一旦等待输入,它就会卡住——没有人能够向它提供任何东西。消耗类型 () 的管道可以等待,但只能获取时钟滴答。

所有这些选项都是合理的。我怀疑 Gonzalez 希望类型系统禁止用户意外地将纯生产者以错误的方式连接到纯消费者,并防止出现难以追踪的错误。通过让纯生产者消费 () 和纯消费者生产 Void,他不可能以错误的方式将它们联系起来。

它实际上是比管道更通用的东西。 ()XHask 类别(以及从 Hask 派生的类别)的 initial and terminal objects , 这意味着 对于所有 Haskell 类型 a,

  • 恰好存在一个态射a -> ()(即const ()
  • 恰好存在一个态射X -> a(即absurd)。

因此,任何函数链 a -> ()>>>() -> b 实际上不能依赖于左侧部分(因为 a -> () 不携带任何信息),并且任何链a -> X>>>X -> b不能依赖正确的部分。从这个意义上说,() -> b 输入被关闭,a -> X 输出也是如此。