如果一个函数的返回类型为⊥(bottom type),则表示它永不返回。例如,在相当普通的情况下,它都可以退出或抛出。
据推测,如果函数的参数类型为⊥,则永远不会(安全)调用它。是否有任何理由定义这种功能?
如果一个函数的返回类型为⊥(bottom type),则表示它永不返回。例如,在相当普通的情况下,它都可以退出或抛出。
据推测,如果函数的参数类型为⊥,则永远不会(安全)调用它。是否有任何理由定义这种功能?
Answers:
其中一个的定义性质的或空类型是存在的函数对于每一种类型的。实际上,存在这样的独特功能。因此,将此功能作为标准库的一部分提供是相当合理的。通常它被称为absurd
。(在具有子类型系统中,这可能是简单地通过具有处理是每一种类型的子类型。然后,将隐式转换absurd
。另一个相关的方法是限定 为,其可以简单地被实例化的任何类型的)。
您绝对希望拥有这样的功能或等效功能,因为这是使您能够利用产生的功能的原因。例如,让我们说,我给一笔型。我对此进行了案例分析,在案例中,我将使用抛出一个异常:E → ⊥。在情况下,我将使用。总的来说,我想类型的值,所以我需要做一些事来把一个成。那就是absurd
让我做的。
就是说,没有太多理由来定义您自己的的函数。根据定义,它们必然是的实例absurd
。不过,如果absurd
标准库未提供它,或者您希望使用类型专用的版本来辅助类型检查/推断,则可以这样做。但是,您可以轻松生成最终实例化为的类型的函数。
即使没有太多理由要编写这样的函数,也通常应该允许它。原因之一是它简化了代码生成工具/宏。
absurd
absurd
throw
为了补充对函数的描述,absurd: ⊥ -> a
我有一个具体示例说明该函数实际上有用的地方。
考虑Haskell数据类型Free f a
,该数据类型表示具有- f
形节点和包含a
s的叶子的常规树结构 :
data Free f a = Op (f (Free f a)) | Var a
可以使用以下功能折叠这些树:
fold :: Functor f => (a -> b) -> (f b -> b) -> Free f a -> b
fold gen alg (Var x) = gen x
fold gen alg (Op x) = alg (fmap (fold gen alg) x)
简而言之,此操作alg
位于节点和gen
叶子处。
到现在为止:可以使用定点数据类型表示所有递归数据结构。在Haskell中Fix f
,可以将其定义为type Fix f = Free f ⊥
(即,具有- f
形节点且在函子外部没有叶子的树f
)。传统上,此结构也有一个折叠,称为cata
:
cata :: Functor f => (f a -> a) -> Fix f -> a
cata alg x = fold absurd alg x
这很好地使用了荒唐的东西:由于树上没有叶子(因为⊥除了以外没有其他居民undefined
),所以永远不可能使用gen
fold来absurd
说明这一点!
底部类型是所有其他类型的子类型,在实践中可能非常有用。例如,类型NULL
在C理论类型安全的版本必须是所有其他指针类型的子类型,否则你无法归还,例如NULL
其中char*
预期; 同样,undefined
理论上类型安全的JavaScript的类型必须是该语言中所有其他类型的子类型。
exit()
throw()
Int
Int
exit()
NULL
是不是单位类型,不同于⊥是空类型?
void*
,则需要为它指定一个可以用于任何指针类型的特定类型。
我可以想到一种用途,并且它被认为是对Swift编程语言的一种改进。
斯威夫特有一个maybe
Monad,拼写为Optional<T>
或T?
。有很多与之交互的方法。
您可以使用条件展开
if let nonOptional = someOptional {
print(nonOptional)
}
else {
print("someOptional was nil")
}
您可以使用map
,flatMap
以变换值
!
,类型(T?) -> T
)强制解开内容,否则触发崩溃nil-coalescing运算符(??
,类型的(T?, T) -> T
)取其值,否则使用默认值:
let someI = Optional(100)
print(someI ?? 123) => 100 // "left operand is non-nil, unwrap it.
let noneI: Int? = nil
print(noneI ?? 123) // => 123 // left operand is nil, take right operand, acts like a "default" value
不幸的是,没有简洁的方式说“解开或抛出错误”或“解开或因自定义错误消息而崩溃”。就像是
let someI: Int? = Optional(123)
let nonOptionalI: Int = someI ?? fatalError("Expected a non-nil value")
不会编译,因为fatalError
具有类型() -> Never
(()
是Void
Swift的单位类型,Never
是Swift的底部类型)。调用它会产生Never
,与T
预期的正确操作数不兼容??
。
为了解决这个问题,Swift Evolution提案SE-0217
-提出了“ 解开或死亡”运算符。它最终被拒绝了,但引起了人们的兴趣,希望成为Never
所有类型的子类型。
如果Never
被设为所有类型的子类型,那么前面的示例将是可编译的:
let someI: Int? = Optional(123)
let nonOptionalI: Int = someI ?? fatalError("Expected a non-nil value")
因为的呼叫站点??
具有类型(T?, Never) -> T
,它将与的(T?, T) -> T
签名兼容??
。
Swift具有“ Never”类型,这似乎与底部类型非常相似:声明为返回Never的函数永远不会返回,带有Never类型的参数的函数永远不会被调用。
这在协议中很有用,协议可能由于语言的类型系统而受到限制,即一个类必须具有某种功能,但不要求必须调用此函数,也不要求参数的类型将是。
有关详细信息,请查看swift-evolution邮件列表中的较新文章。
(x ? 3 : throw new Exception())
出于分析目的,类似的东西会被替换成类似的东西(x ? 3 : absurd(throw new Exception()))
?