底部类型是否有用作功能参数类型的用例?


12

如果一个函数的返回类型为⊥(bottom type),则表示它永不返回。例如,在相当普通的情况下,它都可以退出或抛出。

据推测,如果函数的参数类型为⊥,则永远不会(安全)调用它。是否有任何理由定义这种功能?

Answers:


17

其中一个的定义性质的或空类型是存在的函数A对于每一种类型的A。实际上,存在这样的独特功能。因此,将此功能作为标准库的一部分提供是相当合理的。通常它被称为absurd。(在具有子类型系统中,这可能是简单地通过具有处理是每一种类型的子类型。然后,将隐式转换absurd。另一个相关的方法是限定 α.α,其可以简单地被实例化的任何类型的)。

您绝对希望拥有这样的功能或等效功能,因为这是使您能够利用产生的功能的原因。例如,让我们说,我给一笔型E+A。我对此进行了案例分析,在E案例中,我将使用throw:E抛出一个异常E 。在A情况下,我将使用f:AB。总的来说,我想类型的值B,所以我需要做一些事来把一个B。那就是absurd让我做的。

就是说,没有太多理由来定义您自己的A的函数。根据定义,它们必然是的实例absurd。不过,如果absurd标准库未提供它,或者您希望使用类型专用的版本来辅助类型检查/推断,则可以这样做。但是,您可以轻松生成最终实例化为A的类型的函数。

即使没有太多理由要编写这样的函数,也通常应该允许它。原因之一是它简化了代码生成工具/宏。


因此,这意味着(x ? 3 : throw new Exception())出于分析目的,类似的东西会被替换成类似的东西(x ? 3 : absurd(throw new Exception()))
bdsl

如果你没有子类型或没有定义∀ α α,那么前者将不输入check,而后者将。对于子类型,是的,类似的内容将被隐式插入。当然,你可以把定义内有效地定义什么是∀ α α会。α.αabsurdabsurdthrowα.α
德里克·埃尔金斯

6

为了补充对函数的描述,absurd: ⊥ -> a我有一个具体示例说明该函数实际上有用的地方。

考虑Haskell数据类型Free f a,该数据类型表示具有- f形节点和包含as的叶子的常规树结构 :

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),所以永远不可能使用genfold来absurd说明这一点!


2

底部类型是所有其他类型的子类型,在实践中可能非常有用。例如,类型NULL在C理论类型安全的版本必须是所有其他指针类型的子类型,否则你无法归还,例如NULL其中char*预期; 同样,undefined理论上类型安全的JavaScript的类型必须是该语言中所有其他类型的子类型。

exit()throw()IntIntexit()

TST


3
NULL是不是单位类型,不同于⊥是空类型?
bdsl

我不确定类型理论中theory的含义。
bdsl

1
@bdsl这里的弯曲运算符是“的子类型”;我不确定这是否是标准的,这就是我教授使用的标准。
Draconis

1
@ gnasher729是的,但是C也不是特别安全的类型。我是说,如果不能只将整数转换为void*,则需要为它指定一个可以用于任何指针类型的特定类型。
Draconis


2

我可以想到一种用途,并且它被认为是对Swift编程语言的一种改进。

斯威夫特有一个maybeMonad,拼写为Optional<T>T?。有很多与之交互的方法。

  • 您可以使用条件展开

    if let nonOptional = someOptional {
        print(nonOptional)
    }
    else {
        print("someOptional was nil")
    }
    
  • 您可以使用mapflatMap以变换值

  • 强制解包运算符(!,类型(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()VoidSwift的单位类型,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签名兼容??


0

Swift具有“ Never”类型,这似乎与底部类型非常相似:声明为返回Never的函数永远不会返回,带有Never类型的参数的函数永远不会被调用。

这在协议中很有用,协议可能由于语言的类型系统而受到限制,即一个类必须具有某种功能,但不要求必须调用此函数,也不要求参数的类型将是。

有关详细信息,请查看swift-evolution邮件列表中的较新文章。


7
“快速发展邮件列表上的较新帖子”不是一个非常清晰或稳定的参考。邮件列表没有网络存档吗?
德里克·埃尔金斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.