首先,您需要记住,编程语言倾向于在不同于数学含义的意义上使用“函数”一词。编程语言函数只是一个命名的子例程,它可能产生也可能不产生可以分配或传递的值。
Pascal 在“真实”函数和不返回值的函数之间做了一些区分;关键字procedure 创建一个不返回任何内容的子例程,关键字function 必须返回某种类型的值。
C 是一个模糊这种区别的例子。一个函数可以有一个 void 类型的返回值,它实际上是具有 one 值的类型(尽管该值实际上并没有在代码中表示;你必须假装它存在)。每个具有这种返回类型的函数总是返回相同的值。 Python 使它更明确一点;没有return 语句的函数或没有值的return 语句实际上返回单例值None。
Haskell 有一个类似的类型,(),它由一个同名的值组成。您当然可以定义一系列函数,每个类型一个函数 foo :: a -> () 忽略其参数并返回 ()。它没有任何实用价值(作为纯语言,函数不能做任何事情除了返回其声明的返回类型的值),但它确实有存在。 foo _ = ().
顺便提一下,a -> () 类型的函数将 () 建立为(伪)类别 Hask 中的 terminal 对象,这是建立 所必需的Hask 作为一个笛卡尔封闭的范畴,使其适合定义 Haskell 的语义。
然而,Haskell确实有一个包含 no 值的类型,恰当地称为Void:
data Void
由于Void 是有效类型,您可以想象一个类型包含从Void 到任何其他类型的函数:
absurd :: Void -> a
但是,由于没有 Void 类型的值,因此您不能真正调用这样的函数。 Void -> a 类型的唯一函数可以定义为
absurd x = case x of {} -- There's nothing x *can* match
这并不是说absurd 一点用处都没有,只是没有实际用处。正如a -> () 类型的函数将() 定义为Hask 中的终端对象,absurd 将Void 定义为Haskinitial 对象/strong>。
(这个答案的后半部分是 Bartosz Milewski 精彩的一系列帖子Category Theory for Programmers 中的(糟糕的)信息概要。)