Widening

badge

例如,定义多相关系数如下

ids|T|(x: T, y: T) = x, y

分配同一类的一对实例并没有错 当您分配另一个具有包含关系的类的实例对时,它会向上转换为较大的类并成为相同的类型 另外,很容易理解,如果分配了另一个不在包含关系中的类,就会发生错误

assert ids(1, 2) == (1, 2)
assert ids(1, 2.0) == (1.0, 2.0)
ids(1, "a") # TypeError

现在,具有不同派生类型的类型呢?

i: Int or Str
j: Int or NoneType
ids(i, j) # ?

在解释这一点之前,我们必须关注 Erg 的类型系统实际上并不关注(运行时)类这一事实

1: {__valueclass_tag__ = Phantom Int}
2: {__valueclass_tag__ = Phantom Int}
2.0: {__valueclass_tag__ = Phantom Ratio}
"a": {__valueclass_tag__ = Phantom Str}
ids(1, 2): {__valueclass_tag__ = Phantom Int} and {__valueclass_tag__ = Phantom Int} == {__valueclass_tag__ = Phantom Int}
ids(1, 2.0): {__valueclass_tag__ = Phantom Int} and {__valueclass_tag__ = Phantom Ratio} == {__valueclass_tag__ = Phantom Ratio} # Int < Ratio
ids(1, "a"): {__valueclass_tag__ = Phantom Int} and {__valueclass_tag__ = Phantom Str} == Never # 类型错误

我看不到该类,因为它可能无法准确看到,因为在 Erg 中,对象的类属于运行时信息 例如,一个Int或Str类型的对象的类是IntStr,但你只有通过执行才能知道它是哪一个 当然,Int类型的对象的类被定义为Int,但是在这种情况下,从类型系统中可见的是 Int的结构类型{valueclass_tag = Int}`

现在让我们回到另一个结构化类型示例。总之,上述代码将导致类型错误,因为类型不匹配 但是,如果您使用类型注释进行类型扩展,编译将通过

i: Int or Str
j: Int or NoneType
ids(i, j) # 类型错误: i 和 j 的类型不匹配
# 提示: 尝试扩大类型(例如 ids<Int or Str or NoneType>)
ids<Int or Str or NoneType>(i, j) # OK

A 和 B 有以下可能性

  • A and B == A: 当A <: BA == B
  • A and B == B: 当 A :> BA == B
  • A and B == {}: 当 !(A :> B)!(A <: B)

A 或 B 具有以下可能性

  • A 或 B == A: 当A :> BA == B
  • A or B == B: 当A <: BA == B
  • A 或 B 是不可约的(独立类型): 如果 !(A :> B)!(A <: B)

子程序定义中的类型扩展

如果返回类型不匹配,Erg 默认会出错

parse_to_int s: Str =
    if not s.is_numeric():
        do parse_to_int::return error("not numeric")
... # 返回 Int 对象
# 类型错误: 返回值类型不匹配
# 3 | 做 parse_to_int::return error("not numeric")
# └─ Error
# 4 | ...
# └ Int

为了解决这个问题,需要将返回类型显式指定为 Or 类型

parse_to_int(s: Str): Int or Error =
    if not s.is_numeric():
        do parse_to_int::return error("not numeric")
    ... # 返回 Int 对象

这是设计使然,这样您就不会无意中将子例程的返回类型与另一种类型混合 但是,如果返回值类型选项是具有包含关系的类型,例如 IntNat,它将与较大的对齐。