精神病人思维广

想得太多,读得太少

2021 年 11 月 10 日

后疫情时代,外循环出奇地好,而内循环却不太行了。还请各位在较为宽松的货币政策下,在投资夜壶经济和泡沫经济之余,多多消费拉动内需呀。

消费归消费,该薅的羊毛还是得薅。

请问各位是如何薅羊毛的呢? 元或者 元购物?优惠券满减?又或是信用卡返现?

或者换个问题,在网店买一件商品,最多能享受几次折扣呢?我给出的答案是(至少) 次:

  • 网店平台(京东,淘宝等)提供的折扣
  • 支付渠道(支付宝,微信,云闪付,招行支付等)提供的折扣
  • 支付卡(信用卡,借记卡等)提供的折扣

这三点属于不同层级,所以折扣可以累加。例如你买 块钱的东西,京东提供 ,支付选云闪付,云闪付提供 ,最后信用卡还有个返现或者几毛钱抵扣;那到手价格就是 。且积分也是每个层级分别计算的。

本文的薅羊毛之旅也将围绕这三个层次的折扣展开,并就个人使用情况,做一些推荐。薅羊毛嘛,大体还是图个日常开心吧,毕竟一个月一般也就只能薅到几十块钱。

写给大忙人的结论

因为这个时代大部分人都很忙,所以还是把结论简要写在前面:

  • 第一层次网店平台的折扣,不同平台可能大差不差。且不同用户黏性不同,就提下面亮点,其他不多说。
    • 淘宝的 88vip 很划算。淘宝的省钱月卡看个人情况。
    • 京东会员带有腾讯会员等的一年 左右的比较划算,单买不划算。
  • 第二层次支付渠道的折扣,首推云闪付,次推招行。
    • 云闪付,办理里面的两个 vip 月卡,每个月薅几十块钱。支付宝已经式微。
    • 招行支付,饭票等比旧一代美团每百元至少便宜几块钱。微信支付已经式微。
  • 第三层次支付卡的折扣,我只用过招行,还行。
    • 信用卡我只用过招行的,羊毛还是挺多的;例如 块减几毛的活动基本没断过,且商城里积分可以兑换信用卡还款券。
    • 储蓄卡我基本只用过招行的,羊毛还挺多,每个月固定会送几块钱黄金和几块钱信用卡还款券和几块钱商城券等。(其他银行 app 就用不爽。)

网店平台等提供的折扣

网店实在是算不得新鲜事物了。且各家打斗到现在,提供的折扣也都大差不差了。这里面还要考虑到用户黏性等(各人有各的偏爱),所以本章只说几个我自己用过的觉得划算的吧。

  • 淘宝的 88 vip。随便买卖的老用户 88 块钱一年,带有网易云一年,饿了么一年,优酷一年,以及几十块钱高德优惠券等其他;然后买一部分商品有折扣(估计是天猫的)。且网易云这种支持板绑定他人手机号。我觉得我不上个图的话总有人觉得我在骗人:

    88 vip 可用服务

  • 淘宝的省钱月卡。每个人按月被索取的钱不一样,我是几块钱一个月,就很赚(毕竟我花呗额度只有 ,用户画像应该是屌丝吧……),截至目前为止实打实地省了大概1000 多(找朋友测试过)。看个人情况,如果定力不错、比较理性、愿意对购物按天进行分拆的话十分划算。详细介绍:每天可以领一张 折左右的通用折扣券……各一张,用五张后还能再领一次),有效期是三天。“白天上班忙,没空逛商场”的一个月省下几十块绝对不是问题。而且我已经找朋友测过,我跟他看到的商品价格是一样的,这卡对我没有杀熟,但结算时我可以叠加优惠券。我觉得不上个图的话总有人觉得我在骗人:

    淘宝省钱月卡

  • 京东各种联名 vip(如腾讯视频会员),130 块钱左右一年。京东 vip 的好处:一是很多商品会员价较便宜,二是送免邮券且京东本身物流极快,三是平台反点比较多,所以开了后只在京东买会更赚一些。日常折扣券是 ……这种,每天可以领一张,24 小时有效;需要手动领取(不教,自己搜)。京东声称它帮我省的钱实在是太浮夸了,diss 一下,作为惩罚,不上图。

  • 招行的商城最近几个月经常有 元或者 元白送的垃圾袋、抽纸等;且每个月送一张 元券(可白嫖抽纸)。反正我家的卷纸、抽纸、小包纸巾、垃圾袋基本都是招行送的……信不信由您,抽纸自由:

    招商与抽纸自由,其之一

    招商与抽纸自由,其之二

  • 打车,高德送新人的优惠券挺多的(不送的话就用 12306 里面的高德打),老用户每天 点可以积分兑换,最次也有 块钱减免 的周卡。而且,打车速度快……(我就不打开截图了,留着周末再打开……)

  • 外卖,首推饿了么。饿了么的通用 券基本是永动机:每周周末都有 单送 积分活动,而 积分又能兑 张通用 券。再者,饿了么的券适用范围比美团广,例如奶茶能用。有了招行和饿了么,美团对我来说就只是个骑车软件,车确实还不错。

  • 所有果园都给我滚蛋,特别是 PDD。

支付平台提供的折扣

这可是羊毛最大的地方了。独乐乐不如众乐乐。

  • 云闪付,永远暂时的神。先开个 可享 6 倍积分的那啥 vip ,反正每个月领一张信用卡还款券用掉就回本了。攒着的 6 倍积分大到迪士尼门票,中到网购平台满减,小到信用卡还款券都能换。6 倍积分真的是太多了,我这边随便买买好像就 2-3 万积分了(之后发生退款的话好像积分也不会回收?)……再开个那啥啥 vip,每个月稳定薅十几块钱,想 10 点抢还款券的还有 10 块钱还款券可以拿。这还不美滋滋!趁云闪付还在撒钱攒流量,看我给它薅秃……而且现在云闪付客户不算多,有些券都不用抢的(下图可以看出)。

    云闪付,其之一

    信用卡还款券应该是满 可用(也可能是 )。线下立减估计哪哪都能用吧,反正我公司食堂吃饭都能用……线上支付、指定 app 好像包括了淘宝、京东等,记得在线支付的时候选云闪付全是自动抵扣。

    云闪付,其之二

    积分淘宝满 还是酸爽啊……可以看到,大平台、奶茶店也都有券。支付渠道选云闪付,自动抵扣。哦对了,今年双十一,淘宝买东西,云闪付支付的话满 ,每天 笔,自动抵扣。

    云闪付,其之三

    线下商超包括了罗森、全家等便利店和其他本地便利店,以及大超市。还款券根本同上,根本不用抢。只有下面这个 积分减 块钱的要抢(在某特殊页面里,且 积分是 vip 增送的独立计算的特殊积分)

    云闪付,其之四

    另外云闪付还有售价 元钱的地铁折扣周卡。

  • 招行支付,伺机待发。我就放一张饭票的图,DDDD。其力度,最次每百元也比美团便宜几块钱;其广度,大部分连锁店都有。

    招行饭票

    招行还有影票等。可惜我看电影不是很多,没怎么薅过,未能留下罪证。

    除此之外,招行每个月固定送 3 元12306 券,自动抵扣。送几块钱黄金,多少取决于您存的钱(和金价),我这种人大概是 块钱;送几块钱信用卡还款金,多少取决于您存的钱,我这种人大概是 块钱,送一张商场 元券(可薅纸巾),杭州招行还送全家购物券,一般是满 5 减 5,这个月是满 8 减 8,且每 199 积分可多换一张。好像还有杂七杂八的打车券,公交地铁充值返现等(下图,配合 apple pay + 全国通用的上海公交卡虚拟卡还挺香,毕竟不用解锁手机就能进出地铁站)。

    招行黄金

    Apple Pay 上海公交卡

    最后,积分商城活动的时候别忘了积分兑换信用卡还款金,一般也挺值的,我前几天刚兑了个 元 * 张,每月可用 张。

其实我也没自己找,都是打开 app 弹了个窗,不小心点进去看了看,这一看可不得了……

支付卡片提供的折扣

最后就是支付卡片提供的折扣了。还是推荐搞一张信用卡月均可享受 天免息期

  • 招行的信用卡买苹果产品,官网一般有 期免息。(这还不比花呗和某东白条高到不知道哪里去了?)您问能省多少钱,这只能取决于个人投资能力,有些人 期免息能白嫖,有些人 期免息后可能还亏钱了……(这究竟是为什么呢?因为如果不免息,就不会拿这笔钱去资本市场堵,就不会亏……)
  • 招行的信用卡基本满 减几毛的活动没听过。日常吃饭、外卖刷卡的话,还是能薅一些的。甭管是从云闪付,支付宝,还是微信付的,这些最后如果是使用招行的信用卡支付的话,都是自动抵扣的。

大概也没了,中国的信用卡没啥好说的,不带保险,没啥活动,根本没发展起来(就进入数字支付时代了😂)。

如果感觉用信用卡会多花钱,那么理性方面还需要加强,我就只觉得是白嫖。

于我而言

我觉得我也没花什么时间,就是无意中碰到了云闪付和招行 😂。

(⬇️ 还是投机有意思,这三个月的盈亏完全对冲掉了 ⬇️)

亏冲盈

盈冲亏

类型系统综述

原作者:Luca Cardelli,Microsoft Research

本文改编自 Luca Cardelli 的《Type Systems》的前两章,类型化语言并对内容和顺序做了调整。

在对类型系统分门别类和比较它们的长短处之前,我们不妨仔细读读前人的研究,学究式地讨论与了解

  • 什么是类型?类型化语言与非类型化语言。
  • 类型系统设立的目的,它可以保证什么。
  • 什么是执行错误;安全与行为良好的异同。
  • 需要多大程度的安全——安全与性能的权衡。
  • 如何形式化地描述类型系统。

总述

类型系统的根本目的是防止程序运行过程中出现执行错误,这个非正式的声明激发了对类型系统的研究。它的准确性首先取决于一个相当微妙的问题——什么是执行错误。然而即使解决了这个问题,没有执行错误也是一个相当不简单的性质。我们必须证明所有可以在编程语言中表达的程序都没有执行错误,才能说该语言是类型安全的。(注:原文是 type soundness,译为类型健全;我们使用了更通俗的术语 type safety,即 “类型安全”。)事实证明,在进行相当多的仔细分析后,才可能避免对编程语言的类型健全性提出错误或令人尴尬的声明。而类型系统的分类、描述和研究也已经成为一门正式的学科。

类型系统的形式化需要精确的符号和定义,以及对形式化性质的详细证明(以使人们相信定义确实是妥当的)。有时,这门学科变得相当抽象;但是我们应该始终记住,实用性仍是这些抽象的基本动机:

  • 抽象是出于需要,并且通常可以与具体的直觉相联系;
  • 形式化,作为一门技术,即便不完整地使用也可以达成相当有用的效果。

了解类型系统的主要原则可以帮助语言设计者避免(明显和不明显的)陷阱,看到语言特性彼此之间的独立性或联系,以此使语言设计得更加规范。

如果发展得当,类型系统还可以作为一门工具,以判断语言定义中的(重要部分)是否已经足够明晰——非正式的语言描述(如自然语言,译者注)往往不能足够清晰和详细地说明语言的类型结构,以保证其实现起来没有歧义——同一语言的不同编译器实现了略有不同的类型系统,这是经常发生的情况。此外,许多语言的定义被发现是类型不安全的,即使程序被类型检查器判定为可以接受,也会导致程序崩溃。理想情况下,形式化类型系统应该是所有带类型的编程语言的定义的一部分。这样一来,就可以根据精确的规范来衡量类型检查算法。而且,如果可能与可行的话,整个语言都可以被证明是类型安全的。

接下来,我们会非形式化地介绍类型、执行错误和其相关概念。我们讨论类型系统的预期性质和好处,并回顾类型系统如何被形式化。我们使用的术语并不是完全标准的;这是由于来自不同来源的标准术语的内在不一致性造成的。在提到运行时概念时,我们用动态检查来代替动态类型,并避免使用强类型这样常见但含糊的术语。

类型的基本概念

类型

在程序的执行过程中,一个程序变量可以承担一定的数值(数据)范围。这种范围的上限被称为该变量的类型。例如,一个布尔类型的变量 x 应该在程序的每次运行中只承担布尔值。如果 x 是布尔类型,那么布尔表达式 not(x) 在程序的每次运行中都有合理的意义;如果 x 承载了布尔以外的值,例如字符串,那么布尔表达式 not(x) 并无合理的意义。

类型化语言和非类型化语言

变量可以被赋予非平凡的(即非单一通用类型)类型的语言被称为类型化语言。(原文为 typed languages 和 untyped languages,直译为“类型化的语言”和“非类型化的语言”,译者注。)

不限制变量范围的语言被称为非类型化语言:它们没有类型,或者说,有一个包含所有值的单一通用类型。在这些语言中,操作可以应用于不适当的参数:其结果可能是一个固定的错误值或一个异常等。

类型系统是类型化语言的组成部分,它记录变量的类型;一般来说,它也记录程序中所有表达式的类型。类型系统被用来确定程序是否表现良好。只有符合类型系统的程序才应该被认为是类型化语言的真正程序;不符合类型系统的程序应该被丢弃而从不运行。

显式类型与隐式类型

一种语言由于其类型系统的存在而被类型化,无论类型是否实际出现在程序的语法中。如果类型是语法的一部分,该语言就是显式类型的,否则就是隐式类型的。

没有哪种主流语言是纯粹的隐式类型,但是像 ML 和 Haskell 这样的语言支持编写省略类型信息的大型程序片段;这些语言的类型系统会自动为这些程序片段分配类型。

执行错误、安全、行为良好

典型的执行错误有非法指令错误或非法内存引用错误。然而,还有一些更微妙的执行错误,它们会导致数据损坏却不会被立刻感知。此外,有一些通常不会被类型系统预防的软件故障,例如除以 0 和对 NIL 解引用;也有些语言虽然缺乏类型系统,但软件故障却不会发生。因此,我们需要仔细定义我们的术语。

执行错误和安全

区分两类执行错误是很有用的:

  • 第一类是(在许多计算机架构上)会导致计算立即停止的 trapped 错误,例如除以零和访问非法地址。(注:不太好翻译,因此保留了原单词。)
  • 第二类是在发生时没有被注意到而在之后导致程序任意行为的 untrapped 错误,例如在没有运行时边界检查的情况下,访问超过数组末端的(错误)数据。

如果一个程序片段不会导致 untrapped 错误的发生,那么它就是安全的。所有程序片段都是安全的语言被称为安全语言。因此,安全语言排除了那些可能被忽视的最隐蔽的执行错误。非类型化语言可以通过执行运行时检查来实现安全。类型化语言可以通过静态检查来拒绝所有可能不安全的程序来实现安全(但静态检查通常不会特别精确,因此也会拒绝一些安全的程序)。类型化语言也可以使用运行时和静态检查的混合方式。

尽管安全性是程序的一个重要性质,但类型化语言很少只关注和排除 untrapped 错误,即类型化语言通常也致力于排除 trapped 错误。我们接下来讨论这些问题。

执行错误和行为良好

对于任何给定的语言,我们可以挑选执行错误的一个子集作为禁止的错误(也即编译器必须能检查出的错误,译者注)。禁止的错误应该包括所有 untrapped 错误以及 trapped 错误的一个子集。

如果一个程序片段运行时不会发生任何禁止的错误,那么它就是行为良好的。通过简单推论可知,一个行为良好的程序片段总是安全的。

强(类型)检查

一个所有合法程序都有良好行为的语言被称为强(类型)检查的。

因此,对于一个给定的类型系统,以下情况在强检查语言中成立:

  • 没有 untrapped 错误。
  • 被禁止的 trapped 错误不会发生。
  • 其他 trapped 错误可能会发生;避免这些错误是程序员的责任。

类型化语言可以通过执行静态(即编译时)检查来强制保证行为良好(和安全)。这些语言是静态检查的,检查过程被称为类型检查,而执行这种检查的算法被称为类型检查器。一个通过类型检查器的程序被称为类型良好的;否则,它就是类型错误的——但这可能意味着它就是行为不良的,也可能仅仅意味着类型检查器不能保证它是行为良好的。静态检查语言的例子有 ML、Java 和 Pascal(注意,Pascal 有一些不安全的特性)。

非类型化语言可以通过足够详细的运行时检查来排除所有禁止的错误,来强制保证行为良好(和安全)。例如,它们可以检查所有的数组边界和所有的除法操作,当禁止的错误发生时,产生可恢复的异常。这些语言的检查过程被称为动态检查;Lisp 就是这种语言的一个例子。注意,这些语言仍然是强检查的,尽管它们既没有静态检查,也没有类型系统。

即使是静态检查的语言通常也需要在运行时进行测试以达到安全的目的。例如,一般来说,数组的边界必须在动态中进行测试。因此一种语言被静态检查的事实并不一定意味着可以完全盲目地执行。

一些语言利用其静态类型结构来进行复杂的动态测试。例如,Simula67 的 INSPECT,Modula-3 的 TYPECASE,以及 Java 的 instanceof 结构,都是对对象的运行时类型进行区分。这些语言仍然被认为是静态检查的(稍有不妥),部分原因是动态类型测试是在静态类型系统的基础上定义的。也就是说,对类型相等的动态测试与类型检查器在编译时用来确定类型相等的算法是兼容的。

也许安全性是一个更原始的、比行为良好更重要的性质,然而大多数类型系统的设计是为了确保更一般的行为良好性质,以及隐含的安全性。因此,类型系统的目标通常是通过区分类型良好和类型错误的程序来确保所有程序行为良好。

弱(类型)检查

在现实中,某些静态检查的语言并不能确保安全。也就是说,他们所禁止的错误集并不包括所有 untrapped 错误。这些语言可以被委婉地称为弱(类型)检查(或弱类型,在文献中),意味着一些不安全的操作不会被检测出来。这类语言在弱点的表现上差异很大。例如,Pascal 语言只有在使用未标记的变体类型和函数参数时才是不安全的;而 C 语言有许多不安全的、广泛使用的特性,如指针运算和强制类型转换(casting)。有趣的是,C 语言程序员的十条命令中的前五条 [1] 都是为了弥补 C 语言的弱检查方面的问题。C 语言的弱检查所造成的一些问题在 C++ 中得到了缓解,而更多的问题在 Java 中得到了解决,(这或许)证实了脱离弱检查是一种趋势。Modula-3 支持不安全的特性,但只在明确标记为不安全的模块中,并防止安全模块导入不安全的接口。

大多数非类型化语言都是完全安全的(例如 Lisp)。原因也很简单,如果不是这样的话,编程就会变得非常困难和令人沮丧——程序随时可能崩溃,并且难以发现直接原因。例如汇编语言就是一种“令人不快的”无类型的不安全语言。(相信没有人会享受用它去编写大规模的应用程序,译者注。)

对语言和类型系统的若干讨论

语言应该是安全的吗?

实现安全所需的运行时检查有时被认为太昂贵(例如即使在有大量静态分析的语言中,数组越界等检查通常也不能在编译时完全消除),因此出于性能考虑,有些语言被故意做成不安全的,例如 C 语言。尽管如此,仍有许多人在努力设计安全的 C 语言子集,并制作开发工具,试图通过引入各种(相对昂贵的)运行时检查来安全地执行 C 程序。这些努力来自两个主要的原因:

  • C 语言也被广泛使用在那些对性能要求不高的应用中,
  • 不安全的 C 语言程序所带来安全问题,例如缓冲区溢出和算术下溢,可能导致覆盖任意的内存区域,并可被利用进行攻击。

根据不同的衡量标准,安全相对于性能提升或许是更加经济、有效的。

  • 例如安全的程序在执行错误时下会立即停止,这减少了调试的时间。

  • 安全性保证了运行时结构的完整性,因此可以进行垃圾收集,而垃圾收集大大减少了代码的大小和代码的开发时间。(但牺牲了一些性能。)

  • 安全已经成为系统安全的必要基础,特别是对于加载和运行外来代码的系统(如操作系统内核和网络浏览器)。系统安全正在成为程序开发和维护中最昂贵的方面之一,而安全可以降低这种成本。

因此,安全语言和不安全语言之间的选择最终可能与开发和维护时间以及代码执行时间之间的权衡有关。近年来,安全语言有成为主流的趋势。

语言应该类型化吗?

关于编程语言是否应该类型化的问题仍然存在一些争议。毋庸置疑,用无类型的语言编写的代码很难被维护。从可维护性的角度来看,即使是弱检查的不安全语言(例如 C)也要优于安全但非类型化的语言(例如 Lisp)。下面是从工程的角度提出的支持非类型化语言的论据。

  • 程序执行的经济性。一般来说,编译时准确的类型信息使运行时的一些操作无需昂贵的检查。例如类型信息首先被引入到编程中,以提高代码生成和数值计算的运行效率,Fortran 便是一例。例如在 ML 中,准确的类型信息使得指针解引用时无需做 NIL 检查。
  • 小规模开发的经济性。当一个类型系统设计精良时,类型检查可以捕获很大一部分常见的编程错误,从而减少大量的调试时间。因为大量的其他错误已经被排除,这也使我们更容易观测和调试真正发生的错误。此外,有经验的程序员还可以采用特定的编码风格来使一些(隐式的)逻辑错误成为(显式的)类型错误:他们把类型检查器作为一种开发工具。
  • 编译的经济性。类型信息可以被组织成程序模块的接口。然后,模块可以相互独立地进行编译,每个模块只依赖于其他模块的接口。这使得编译大型系统更加高效,因为在接口稳定的情况下,对一个模块的修改不会导致其他模块的重新编译。例如 Modula-2 和 Ada 便是这样。
  • 大规模开发的经济性。接口和模块对代码开发有方法上的优势。大型的程序员团队可以协商要实现的接口,然后分别去实现相应的代码片段。代码片断之间的依赖性被最小化,代码可以被局部地重新安排,而不用担心全局的影响。(这些好处也可以通过非正式的接口规范来实现,但在实践中,类型检查对验证对规范的遵守有很大帮助)。)
  • 安全领域的开发和维护的经济性。正如安全对于消除缓冲区溢出等安全漏洞是必要的,类型化对于消除其他灾难性的安全漏洞也是必要的。这里有一个典型的例子:如果有任何方法(不管多么复杂)可以将一个整数转换成一个指针类型(或对象类型)的值,那么整个系统就会面临风险——攻击者可以在系统中的任何地方访问任何数据。另一个有用的(但不是必须的)技术是将一个给定的类型的指针转换成一个整数,然后再转换成上述不同类型的指针。从维护和整体执行效率的角度来看,消除这些安全问题的最经济有效的方法是采用类型化语言。不过,安全问题在系统的各个层面都是一个问题,因此类型化语言是一个很好的基础但仍不是一个完整的解决方案。
  • 语言特性的经济性。类型结构天然以正交的方式组成。例如,在 Pascal 中,二维数组其实是数组的数组;在 ML 中,有 n 个参数的函数其实是一个具有单个 n-元组参数的函数。因此,类型系统可以促进语言特性间的正交性,让人们思考和质疑(语言中)人为做出的一些限制,从而倾向于降低编程语言的复杂性。

类型系统的预期性质

在本章的其余部分,我们假设语言应该既安全又类型化,因此应该采用类型系统。在对类型系统的研究中,我们并不区分trapped 或 untrapped 错误,也不区分安全和行为良好:我们专注于行为良好,并将安全作为一个隐含的性质。

在编程语言中,类型通常具有实用性特征,这些特征将其与其他种类的程序标注区分开来。一般来说,关于程序行为的标注可以是非正式的注释,也可以是需要经过验证的正式的规格描述。类型位于这个范围中间:它们比注释更精确,比规格描述更容易机械化(自动化)。以下是任何类型系统都被期望具有的基本性质:

  • 类型系统应该是可验证的:应该有一种算法(类型检查算法)可以确保程序的行为良好。类型系统的目的不是简单地说明程序员的意图,而是在执行错误发生之前主动捕捉它们。(任意的形式化规范不具备这些性质。)
  • 类型系统应该是透明的:程序员应该能够轻松地预测一段代码是否可以通过类型检查。如果它不能通过类型检查,那么失败的原因也应当是不言而喻的。(自动定理证明不具备这些特性。)
  • 类型系统应该是强制的:应该尽可能地静态检查关于类型的声明,并动态检查无法静态检查的部分。程序与其类型之间的一致性应该被例行验证。(程序注释不具有这些性质。)

类型系统是如何被形式化的

我们怎样才能保证类型良好的程序是真正行为良好的呢?或者说,我们怎样才能确保语言的类型规则不会意外地放过行为不良的程序呢?

一般的程序语言手册都会或多或少地描述它们的类型系统,而形式化的类型系统是正是那些非正式类型系统的数学特征。一旦一个类型系统被形式化,我们就可以尝试证明一个正确性定理(soundness theorem,似乎一般称为健全性定理),说明类型良好的程序一定也是行为良好的。如果这样的正确性定理成立,我们就说该类型系统是正确的。(即“类型化语言中的所有程序都行为良好”。)

为了使一个类型系统正规化,并证明其正确性定理,我们必须对整个语言进行规范化,正如下文描述的那样。

  • 第一步是描述它的语法。对于大多数语言来说,这可以简化为描述类型和项(terms,一般为语句、表达式和其他程序片段)的语法。类型表达关于程序的静态知识,而项则表达算法行为。

  • 第二步是定义语言的作用域规则,将标识符的每次出现与它们的声明位置(无歧义地)地绑定起来。类型化语言所需的作用域绑定总是静态的,也就是说,所有标识符的绑定必须在运行前唯一确定。绑定关系通常可以纯粹从语言的语法中确定,而不需要任何进一步的分析。静态作用域也被被称为词法作用域(lexical scoping),缺乏静态作用域的情形被称为动态作用域(dynamic scoping)。正式的作用域绑定规则可以通过定义程序片段的自由变量集来给出,之后就可以定义如何去替换类型或项中的自由变量(substitution)。

  • 当这些都解决后,我们可以继续定义语言的类型规则“has-type”。这些规则描述了项 和类型 之间的形式为 的关系(即 有类型 )。一些语言还会定义类型上的形式为 的子类型关系以及形式为 等价关系。语言的类型规则的集合即为它的类型系统,具有类型系统的语言被称为类型化语言。

  • 除此之外,我们还需要引入一个不反映在语言语法中的基本要素——静态类型环境——否则类型规则就不能被形式化,因为类型规则总是针对被类型检查的片段的静态环境而制定。在程序片段的处理过程中,这个静态环境被用来记录自由变量的类型。在类型检查阶段,它与编译器的符号表紧密相关。例如,前文中的类型关系 与静态类型环境 相关联,该环境包含了 中的自由变量的信息。(译者注:例如有 bool 类型的全局变量 x,那么在检查全局函数 int main() { x + 1; } 的函数体时,类型环境 将包含类型关系 ,其中 x 是函数体中的自由变量。)

  • 形式化语言的最后一步是将其语义定义为项和项的计算结果之间的 “has-value” 关系。这种关系的形式在很大程度上取决于所采用的语义风格(例如小步语义,大步语义,译者注)。在任何情况下,语言的语义和类型系统都是相互关联的:项的类型和其计算结果的类型应该是相同的(或有其他适当形式的关联)——这就是正确性定理的本质。

类型系统的基本概念几乎适用于所有编程范式(函数式、命令式、并发式等等);对于不同的范式,一些类型规则往往可以不加改变地使用。例如,无论编程范式是命令式还是函数式,无论语义是按名调用(call-by-name)还是按值调用(call-by-value),函数的基本类型规则都是一样的。

关于类型等价:如上所述,大多数非平凡的类型系统需要定义一个类型上的等价关系。这是定义编程语言时的一个重要问题:什么时候独立给出的类型表达式是等价的?例如,

1
2
type X = Bool
type Y = Bool

如果上述类型名 X 和 Y 由于相等,我们就有了结构等价。如果它们不相等,我们就有按名等价

在实践中,大多数语言都既使用了结构等价又使用了按名等价。单纯的结构等价可以通过类型规则轻松而精确地定义,而按名等价则更难确定,而且往往有一种算法的味道。当类型化的数据需要在网络上存储或传输时,结构等价具有独特的优势;相比之下,按名等价不容易处理那些在时间或空间上分别开发和编译的交互程序。

在本章中,我们讨论了独立于语义的类型系统。不过,我们应该知道,最终一个类型系统必须与语义相关,而且正确性理论应该对该语义成立。

用以描述类型系统的语言

类型系统指定了编程语言的类型规则,但它又独立于特定的类型检查算法;正如形式化的语法可以描述编程语言的语法,但它又独立于特定的语义解析算法。

将类型系统与类型检查算法脱钩既方便又有用:类型系统属于语言定义,而特定算法则属于编译器。用类型系统来解释语言的类型方面——相比于用某个编译器所使用的算法来解释——总是较为容易的。此外,不同的编译器可能对同一个类型系统使用不同的类型检查算法。(作为一个小问题,从技术上讲,可以定出只存在实际不可行的类型检查算法或者根本就不存在类型检查算法的类型系统;然而,通常的意图是允许有效的类型检查算法。)

断言

对一个类型系统的描述始于对断言(judgements)的形式化描述。一个典型的断言形式如下(其中 意为蕴含(英文为 entails):

这里 是一个静态类型环境;例如,它可以是一个由(不同)变量及其类型组成的有序列表 。空环境用 表示, 中声明的变量 的集合用 表示,它即为 的定义域。 的形式因断言而异,其中的所有自由变量都必须在 中声明。

就我们目前的目的而言,最重要的断言是关于类型关系的断言,它表明一个项 在静态类型环境下具有类型

示例:

其他的断言形式往往也是必要的。一个常见的断言形式是指明一个环境是构造良好的(well-formed):

任何给定的判断都可以被看作是有效的(如 )或无效的(如 )。有效性形式化地描述了类型良好这一的概念。有效断言和无效断言之间的区别可以用多种方式表达,但这种描述合法断言的方式已经成为主流。此外,类型规则是高度模块化的:不同语言要素的规则可以单独编写(这与单一、巨大、复杂的类型检查算法相反)。因此,类型规则相对来说更容易阅读和理解。

类型规则

类型规则在其他已知的有效的断言的基础上说明某一断言的有效性。这个过程通常由一些本质上一定有效的判断(通常是 ,它表示空环境是构造良好的)开始。

每条类型规则都写成横线之上的若干前提判断 ,横线之下是单一的结论判断。当所有的前提得到满足时(前提的数量可以是零),结论必须成立。每条规则都有一个名称。(按照惯例,名称的第一个字由结论中的断言决定;例如,”(Val …) “形式的规则名称是指其结论为关于值(即项的计算结果,译者注)的类型断言的规则。) 当需要时,限制规则适用性的条件,以及规则中使用的缩写,都会在规则名称或前提旁边加以注释。

例如,下面两条规则中的第一条指出,在任何结构良好的环境 中,任何数字都是一个 类型的表达式。第二条规则指出,表示自然数的两个表达式 可以组合成一个更大的表达式 ,它也表示自然数。此外, 的环境 声明了 中的所有自由变量的类型,并且也对 有效。

类型规则二例

类型规则的集合被称为(形式化的)类型系统。从技术上讲,类型系统符合形式化证明系统的一般框架:用于进行逐步演绎的规则的集合。在类型系统中进行的演绎关注程序的类型化。

类型的推导

在一个给定的类型系统中,一个推导过程(derivation)是一棵断言树,上面是叶子,下面是根,其中每个断言都是通过系统的一些规则由紧挨着它的断言得到的。对类型系统的一个基本要求是,必须能够检查一个推导是否被正确地构造。

在一个给定的类型系统中,一个有效的断言是通过正确地使用类型规则得到的,因此它一定是作为某个推导的根得到的断言。例如,使用前面给出的三条规则,我们可以建立下面的推导,从而确定 是一个有效的判断。每一步应用的规则都显示在每个结论的右边。

类型推导一例

类型良好和类型推断

在一个给定的类型系统中,如果在环境 下,存在一个类型 ,使得 是一个有效的断言,那么项 就是类型良好的。

探索一个项的推导过程(也就是类型)被称为类型推断问题(type inference problem)。在上述简单类型系统中,可以在空环境下为项 推断出一个类型。通过前面的推导,这个类型是

假设我们现在添加一个类型规则,其前提为 ,结论为 。在该类型系统中,因为不存在自然数与布尔数相加的规则,所以我们无法推断出项 的任何类型。由于 没有任何推导过程,我们说 是不可类型化的,或者说它是类型错误的,或者说类型化过程有错。

我们可以进一步增加一个类型规则,其前提是 ,结论是 (例如,意图是将 解释为)。在这样一个类型系统中,可以为术语 推断出一个类型,它是类型良好的。

因此,一个给定项的类型推断问题对有关的类型系统非常敏感。根据类型系统的不同,类型推断的算法可能非常容易,也可能非常困难,甚至不可能找到。如果找到了,最好的算法可能是非常有效的,也可能是慢得无可救药的。尽管类型系统被表达出来,并且经常被抽象地设计出来,但它们的实际效用取决于是否有好的类型推断算法。

像 Pascal 这样的显式类型的程序性语言的类型推断问题是相当容易解决的,隐式类型语言如 ML 的类型推断问题要微妙得多:而在多态(polymorphism)存在的情况下,类型推断问题变得特别困难。Ada 和 Standard ML 的显式类型多态结构的类型推断问题在实践中是可以处理的;然而,这些问题基本上是由算法解决的,而没有描述它们的类型系统。适用于多态的最纯粹和一般的类型系统是由 λ-演算给出的(之后介绍)。

类型正确

现在我们已经建立了所有关于类型系统的一般概念,可以开始研究特定的类型系统了。之后我们会回顾一些非常强大但又比较理论化的类型系统:我们首先了解这些特定的类型系统,这样就可以更容易地为编程语言中可能遇到的各种复杂的特性编写类型规则。

当我们沉浸在类型规则中时,我们应该记住,一个合理的类型系统不仅仅是一个任意的规则集合。类型良好是为了对应于行为良好这一语义概念。人们习惯于通过证明类型正确性定理来检查类型系统的内部一致性,这就是类型系统与语义交汇的地方。对于指称语义(denotational semantics),我们期望如果 是有效的,那么 就成立( 的值属于类型 所表示的值的集合)。对于操作语义,我们期望如果 以及 归约(reduce,可以简单理解为计算,译者注)为 ,那么 也成立。在这两种情况下,类型正确性定理都断言了类型良好的程序在计算时不会出现执行错误(这里忽略证明)。

在本系列的下一篇文章中,我们会介绍一些主流编程语言中常见的类型系统和其中的类型规则。


微信公众号 编程语言Lab 上也有:
https://mp.weixin.qq.com/s/GJsR_P7xS9h2pP4z8mjvcg


MathJax 渲染有点奇怪,最终本文内的所有 \mathit{Nat} 都改为了 Nat

2023 年留言:别看了,亏麻了。

2021 年 9 月 5 日

继上次写的“投资指南——新韭篇”后,又过去了 4 个月。这四个月中我的运气还挺好的,白酒、医药、芯片、5G、新能源车、光伏、军工的便宜我都捡到了……但又因为不敢梭哈,所以这四个月间基金的总收益大概增长了 12%。当初说好的定投宽基呢

首先,我等新韭不能高估了自己心态。因此本文先谈谈心态这事儿,之后说既然这种心态不可避免,如何投资会更稳妥些。

我们的心态没自己想象的那么好

下跌时

大家在买基金或者股票开户前,应该都做过“风险承受能力”问卷。问卷中往往会有些题目问我们

  • 投资期限,以及
  • 能承受本金多大幅度的亏损。

我猜很多人或许跟我一样,填了“投资期限2年+”以及“可以承受本金20~50%的亏损”(甚至 50% 以上的亏损)。现在回想起来,大概只是个笑话。回想一下:

  • 1 天跌超过 5% 是什么感觉?
  • 连续跌 7 天是什么感觉?
  • 1 个月跌 30% 是什么感觉?
  • 半年甚至一年不回本是什么感觉?

发生上述情形时,如果想撤资,那就是承受不住呀——特别是我们觉得投资方向和方式没有错的时候。毕竟,对新人来说,2 年以上的投资和忍受浮亏 50% 谈何容易。

上涨时

另一方面,在上涨时:

  • 1 天涨超过 5% 是什么感觉?
  • 连续涨 7 天是什么感觉?
  • 1 个月涨 30% 是什么感觉?

这时候有想过撤资吗?其实这时候撤资无非就是想赚更多:觉得涨得过分了会跌回去,先卖出等跌回去了再买。

应对策略

本文不是鸡汤文,所以也不会谈所谓的“改善心态”。本文要谈的是“如何系统性地控制回撤(即浮亏)”。

方法一:选择一只收益率平滑的老牌基金

下图是美国的桥水全天候基金与标普 500 被动基金的收益对比。

桥水全天候基金与标普 500

第一眼看去,1991~2015 年间桥水全天候基金并没有比单一标普 500 被动基金多出任何收益(这既是很多投资理财书籍的观点,也是我几个月前所持的观点)。但仔细看图,我们还可以发现桥水全天候基金的曲线比标普 500 平滑得多。对个人投资者来说,这通常意味着

  • 股市大跌时没那么难以忍受和想卖出,
  • 股市大涨时没那么难以忍受和想卖出;
  • 任何时候大额买卖都是相对安全的。

这其实对还没入场的(小富即安的)新人是非常友好的。

这里举的是美股的例子,而且这个基金我们一般人不好买到。在癌股里,怕麻烦的可以直接选债基基金,这种基金平均年化一般在 6%~12%,收益越低波动越低,收益越高波动越高。这种基金其实挺多的,这里就不做推荐了。(但考虑到资金流动性,我觉得还是 6 个月内可 0 费率赎回的比较好。)

方法二:用纯债和股票组合出自己的债基基金

不怕麻烦的可以自己按比例选择一些纯债和股票基金。

虽然与法一相比麻烦了许多,但也有组合更加灵活、选择更加宽泛的优势:

  • 纯债可以随时投入。
  • 股票型基金可按自己喜好选择。

方法三:题材、大小盘对冲

我们还可以考虑一些不同涨同跌的股票型基金来平滑收益率曲线。

  • 比如新能源和基建房地产就有点对冲的味道。(260112 有点意思(但也只是有点意思。)。)
  • 上证主板科创创业版是不是也有点对冲的味道。
  • 大盘蓝筹(如上证 50,上证 100,沪深 300)和中小盘成长(中证 500,中证 1000)是不是也有点对冲的味道。再比如中美是不是也有点对冲的味道?。

涨了几年的大盘蓝筹最近几个月是不涨反跌,但最近 3 个月中证 500 涨了 20% 了吧,目前点位处于 2015 年股灾时的 25% 位置。创业板那就涨得更多了。

方法四:长短线对冲

“你是左侧买入还是右侧买入?”

  • 左侧买入是指在下跌趋势时觉得超跌了,去抄底,提前布局。但一般是抄在山腰上,还会继续下跌。所以这种方式一般赚的是半年一年甚至两年后的钱,比较长线;因此左侧买入一般也可以叫价值投资。新手切记第一次抄底不要梭哈。
  • 右侧买入时指在上升趋势时买入,稍微赚一笔,小富即安。这种要注意不能太贪,根据买入时机和题材,一般上涨 10%~15% 时就是个会回调的小高点了。同样,历史高点时不建议梭哈。(本人通过这种方式在今年的热点题材上赚了不少。)在癌股,热点题材还是要追的吧,送上门的钱为什么不要?

其实就是那句话吧:不要和趋势作对,在上升期中赚钱是容易的,在下降期中赚钱是困难的;所以我认为追涨杀跌也不无道理。这种长短线对冲,往往能淡化一些长线布局带来的临时损失。

另外,我个人的理念是尽量不在左侧买入。价值投资也是在横盘处买入。

方法五:全球视野

分散一些资金到香港、美国市场。根据自己喜好选择制造业或者互联网,不过一般互联网居多。

  • 大家听到烂的纳斯达克 100 指数、标普 500 指数。
  • 恒生指数、恒生互联网指数。
  • 中概互联网指数。

注 1:最近中概互联网是真的低,在我上次抄底(失败)后又跌了 30%,近乎腰斩。

注 2:喜欢玩的还可以看看美国基建房地产呀,美国 REIT 还没恢复到疫情前水平。

注 3:玩美股新手就不要想着低买高卖了,定投就好了,反正一直涨,除非国际形势出幺蛾子跌了 20% 什么的。

方法六:买 “N 年定开”或 “N 年持有”

有一小撮基金是不能随意买卖的,一般有“N 年定开”和“N 年持有”的标注。

  • N 年定开是说每 N 年后,在固定期限内开放买卖。
  • N 年持有是说每次买入后的份额,持有 N 年后才能卖出。

管不住自己手的可以买这种基金,然后把软件删了就行:反正涨了卖不掉,跌了甚至也买不了,就当不存在吧。

但缺点也显而易见:流动性大为降低。不过也有不到一年的,可以考虑一下。

另外,喜欢补仓的请买 N 年持有而不是 N 年定开。记性不好的也得买 N 年持有。N 年定开到时候忘了卖也不是闹着玩的。

(我算是交足了学费,买了个 N 年定开的目前亏损 20% 多还不能补仓。)

个人对未来癌股的看法

个人感觉癌股市场挺像政策市场的。国家规划要发展啥就要发展啥,甚至有点其他赛道的都不许涨的味道。这一点从最近的央视新闻也能看到:教育、游戏、白酒样样被锤。

  • 过去五年涨了大盘蓝筹(和白酒)。
  • 未来五年应该是科技。至少未来两年科创板创业板肯定是主航道:新能源车、光伏、芯片、医疗。未来大概属于科技和高端制造业。
  • 癌股开放了外资基金,以后资金量应该会更大。(近期贝莱德就搞了个 80 亿的基金,让我们看看水土服不服,是不是也要被割哈哈哈。)

这半年,反正我定投的大盘蓝筹这半年没赚到一分钱,反而赔了几个点。低吗?比炒上天的热门赛道低多了,个个近乎腰斩,但就是不赚钱。

近期出现了主板创业板轮动的局面,比如主板涨几天跌几天,科创创业板涨几天跌几天;板内甚至出现题材快速轮动的局面。这时候追涨杀跌就是作死。但是多面埋伏,逢高卖出就赚翻了。

  • 白酒:近期有企稳的现象,喜欢抄底的朋友们可以买一点。
  • 医药:不稳,轮动下跌,观望为主。感觉资金在流出。
  • 军工:一个月涨了 30% 又跌了 10%,感觉资金在流出。
  • 芯片:一个月涨了 30% 又跌了 10%,感觉资金在流出。但这些企业的估值吧,一言难尽。(而且国家大基金早就撤资了。)
  • 光伏:资金进进出出,最近好像又在进。这是 toB 市场,年增速 20% 稳的。但这些企业的估值吧,一言难尽。
  • 新能源:资金进进出出。这是 toC 市场,可能有爆炸增长,但这些企业的估值吧,一言难尽。

总结

新人心态容易爆炸,请做好风险对冲:

  • 怕麻烦就选债基打底的基金。
  • 不梭哈在同一个股票型基金
  • 不梭哈在同一个国家的市场。
  • 大小盘结合:大盘蓝筹、中小盘成长要有配比。
  • 长短线结合:既要使用定投赚一两年后的钱,也要赚当下的钱。
  • 题材结合:热点题材多布局,轮动的时候逢高卖出。
  • 交易时机:短线趋势“追涨杀跌”,长线“追跌杀涨”,轮动和震荡“追跌杀涨”。
  • 实在不行买定开,然后删软件。

另外,最近红利低波开始涨了;还有,B 圈不也可以图个乐子考虑一下吗?

附录 II:明灯(冥灯)

冥灯自然不构成投资建议。

债基、债券

  • 000037
  • 003547
  • 001868

宽基指数(沪深 300 太多,自己找)

  • 中证 500 502000,001557
  • 中证 1000 013331
  • 中证 100 010351
  • 科创创业 012895
  • 纳斯达克 040046
  • 标普 161125
  • 美国 REIT 160140

定投且稳定亏损中(你们博士真的不行啊)

  • 富国天惠 003494
  • 华商红利 000279

其他

  • 房地产 519191
  • 白酒 012414
  • 军工 011148
  • 动漫游戏 012769
  • 化工 012538
  • 养猪 012725
  • 能源基建 260112
  • 新能源替代品 001532
  • 中概互联网 006328
  • 012608
  • 医疗 010685
  • 芯片 008888
  • 随便玩玩 003567,004235,161031,003305,012771,001605

附录 I:近期时间加权收益率

对比上证指数:

对比上证指数

对比沪深 300:

对比沪深 300

对比 中证 500:

对比中证 500

对比创业板:

对比创业板

垃圾软件没提供资产加权收益率。

浅谈 Intersection Types

序一:本来打算写“浅谈 Intersection Types 和 Union Types”的,后来考虑到前者的篇幅已经有点长了,就砍成了“浅谈 Intersection Types”,后半部分以后再谈。

序二:网上其实有挺多关于 intersection types (和 union types)的文章了,且大部分是以 TypeScript 为宿主语言进行说明的。不过那些文章比较侧重这两种类型在 TypeScript 中的使用方式,对这以外的知识鲜有说明,本文旨在弥补这一不足。

本文大量参考了《Programming with Intersection Types and Bounded Polymorphism》《Row and Bounded Polymorphism via Disjoint Polymorphism》,或者直接说翻译吧,更贴切些,毕竟也没有太多自己的原创研究。

Intersection 类型

Intsersection 类型太长,后文一律简称 I 类型。

相信大家在生活中经常会看到 “既要……,且要……,还要……” 这样的句式,I 类型正是又一例证。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
interface Fly {
void fly();
}
interface Swim {
void swim();
}
interface Run {
void run();
}


class C<T extends Fly & Swim & Run> {
T omni;
void test() {
omni.fly();
omni.swim();
omni.run();

omni.run();
omni.fly();
}
}

如上的 Java 代码, T extends Fly & Swim & Run 部分引入的类型 T 本质上就是一个 I 类型 ,意即 T “既要能飞,且要会游,还要善跑”。可以看出,在函数 test 函数中,T 类型的 omni 确实展现出了惊动自然的能力,先后表演了飞翔、潜泳、疾驰,最后连跑带飞的溜了。刚开始介绍,不要嫌我啰嗦:“所以说,正因为 T 这个类型兼具了 FlySwimRun 三个类型的所有能力,定义在这三个类型里的函数才都可以被调用;如果这三个类型有成员的话,自然也都是可以被访问的。”

我们不禁要问,这样的传奇真的存在吗?还好,在代码中我们可以直接无中生有,例如

1
2
3
4
5
class Omnipotence implements Fly, Swim, Run {
void swim() { /* do something */ };
void run() { /* do something */ };
void fly() { /* do something */ };
}

便硬生生地造了一个满足要求的类型 Omnipotence,对造物主来说真是小事一桩。

言归正传。下文中我们使用 & 作为 I 类型的类型构造器。& 接受两个类型(其实一组类型也可以,但本文想尽量简单些),并构造出单个 I 类型:

1
& : (Type, Type) -> Type  // 意会一下,我们就不引入 kind 的概念了

上文中 T 的类型可表示为 T : Fly & Swim & Run

& 像加法一样具有结合律,所以实现的时候不太需要关注它到底是左结合的还是右结合的,随意啦。

如果还是觉得不好理解的话,不妨回想一下函数类型 T -> R,我们可以说 -> 是函数类型的类型构造器

1
2
3
-> : (Type, Type) -> Type

// 把 Int -> Bool 理解为 ->(Int, Bool)的结果好了

I 类型的简要介绍到此为止,下面我们谈谈它与参数化多态特设多态函数重载非确定性(链接是非确定性图灵机的,凑合一下)的关系。

I 类型与函数重载

如果同一个作用域下的函数 f 定义了好几次且构成重载,那么 f 具有什么类型呢?我想聪明的读者应该已经想到了,很明显,f 可以具有 I 类型。

1
2
f : Int -> Int
f : Double -> Double

我们不妨把 f 的类型写成 Int -> Int & Double -> Double。为了少写几个括号,我们假设 -> 的结合性比 & 更强。

这样的 f,既可以处理整数,也可以处理浮点数,因此 f(1) (假设 1 不符合浮点数语法)与 f(1.0) 均为合法的调用——前者选用 Int -> Int 的逻辑处理,后者选用 Double -> Double 的逻辑处理 。

如果再给 f 新增一个能处理布尔类型的定义

1
f : Bool -> Bool

那么函数调用 f(true) 也将是合法的,此时 f 的类型将变成 Int -> Int & Double -> Double & Bool -> Bool

那么 I 类型可以替代重载吗?我认为取决于需求。

I 类型的类型检查与非确定行为

根据《Programming with Intersection Types and Bounded Polymorphism》,I 类型的类型检查会“穷尽每一种可能”。在做函数调用 f(t)时,如果 ft 都是 I 类型,那么每一组可以“匹配”的类型都会产生一个结果类型;如果“匹配”的类型超过一个,那么函数调用的结果类型仍然是一个 I 类型,例如:

1
2
3
4
f : Bool -> Bool & Bool -> Int
t : Bool

f(t) : Bool & Int

因为 fBool -> Bool 部分和 Bool -> Int 部分都可调用,所以它们会分别接受参数 t 并且生成结果。生成的结果亦使用 & 相连组成一个 I 类型。

这里便可以看出传统的 I 类型检查中的函数调用与常见的函数重载的不同了:函数重载会根据参数类型选取一个特定的、在某种条件下“最优”的 f 类型,如果无法选取则报错——例如此例中,没有其他更多信息时 f(t) 这个调用是要报类型错误的,而 I 类型会枚举并尝试每一种可能。

下面再看另一个例子,加深对 I 类型的函数调用的理解:

1
2
3
4
f : Bool -> Bool & Int -> Int & Double -> Double
t : Bool & Int

f(t) : Bool & Int

这里函数本身和入参都是 I 类型。入参 t 既有 Bool 的特质也有 Int 的特质:

  • t 呈现 Bool 的特质时,f : Bool -> Bool 这部分特质是可应用、“匹配”的,函数调用的结果为 Bool 类型;
  • t 作为 Int 时,f : Int -> Int 这部分特质时可应用的,函数调用的结果为 Int 类型。

因此 f(t) 的结果仍然为 I 类型的 Bool & Int。此即为 I 类型的类型检查中最“有趣”的部分。

由此我们还能得到另一个结论,I 类型与函数重载都是特设多态,而不是参数化多态,因为其对不同入参类型的处理逻辑不同。

如何构造 I 类型的数据

如果 I 类型是由函数类型组成的,似乎我们还可以通过多次定义同名函数来实现;但上文中我们使用了 t : Bool & Int 这种神奇的数据,那么这种数据是如何构造出的呢?

答案其实很简单,也很复杂。简单性在于只需要引入一种新的数据构造语法:正如(,) 语法可构造出 tuple 类型,我们可以新增例如 (,,)这样的语法来构造出 I 类型的数据。

1
2
3
4
5
6
7
true : Bool

1 : Int

(true, 1) : Bool * Int // tuple type

(true ,, 1) : Bool & Int // intersection type

而复杂性在于其背后的实现,(我猜)没有 IR 支持 I 类型,所以翻译到 IR 前,表层语言的 I 类型需要通过“解糖”或者其他(elaboration)手段消除,而这份工作显然没有那么轻松。

I 类型与 Bounded Quantification

简单的 I 类型与简单的 bounded quantification 是可以互换的。看下面的函数定义

1
2
3
4
5
6
7
class C {}

Bool f<T extends C>(T) { return true }
// f : <T <: C>. T -> Bool

Bool f<T>(T & C) { return true }
// f : <T>. (T & C) -> Bool

直观来看,其实这两个函数定义说了同一件事:别管实际的入参是什么类型,它都要具备 C 类型的所有特质——即为 C 类型的子类型——其他特质我不管。

由于本文只是入坑指南,更详尽的例子和解释请参考文中提到的论文。

其他话题

  • I 类型与 tuple 类型很像,但显然不等价,它们的关系是?
  • I 类型该如何设计,是否要具有幂等性?
  • I 类型该如何设计,是否要求组成类型没有交集?(是否要设计成 disjoint intersection types)
  • 如果类型系统等价于逻辑系统,那么 I 类型是否等价于逻辑中的“与”?还是说 tuple 类型才是逻辑中那个深入人心的“与”?
  • ……

问题总比方法多,欢迎各位观众老爷前往挑战 I 类型。


微信编程语言 Lab 公众号也有转载。

https://mp.weixin.qq.com/s/A_hiVgZCOpPka700eLLiRg

2021 年 7 月 24 日

大暑前后,台风洪水比较多。前几天是郑州和周边城市被干,今天是杭州周边城市被干。本着不给防风防洪防汛添乱的原则,周末就宅在家里哪也没去。

其实本文也没什么实质性内容,就是随便查查,查完随便喷喷,喷完随便写写,写完随便删删。激发我查阅兴趣的主要有三点:一是在家闲的,难得的时间;二是在杭州滨江区生活了一年多。因为一到雨天就看见柏油马路就到处是水坑,又见过三次排水口被堵(其中一次水已没过人行道,在创伟科技园旁边),一直觉得滨江区排水不畅;总觉得郑州那个雨量换到滨江的话,滨江应该已经成亚特兰蒂斯了。三是对郑州这百年一遇乃至“千年遇一”的自然灾害比较好奇。

目录

  • 郑州、河南历史上的雨灾
  • 东京的排水能力
  • 杭州对烟花的应对方式
  • 宁波余姚水灾与“浙江滚出中国”
  • 我对杭州的感观:杭州的基建与发展

郑州被干

郑州的降雨量确实大,7 月 20 日 16 时至 17 时,峰值雨量为 201.9mm,而 24 小时平均雨量达 253mm。可能很多人(尤其是文科生(没有贬低的意思))对降水量没有什么概念,不过可以回想起 400mm 等降水线的概念,这个概念告诉我们很多地方一年只下 400mm的雨——即使一年只有少到 50 个雨天,那平均每个雨天只有至多 8mm 的降雨,253mm 就是30多倍。或者这么看,郑州以往的平均年降雨量为 640.8mm,而近三天的降雨量为617.1mm,三天顶一年。

河南不是第一次被干,这次的雨也不是最大的

我很好奇这么大的雨是不是史无前例,就顺着微博上的 758 关键字查了查。原来 758 是指 1975 年 8 月的一场洪水。1975 年 8 月,河南驻马店不幸承受了史称 758 特大洪水的灾难。当年的 8 月 5 日至 6 日,板桥雨量站测得日降雨量为 448.1mm(不过最大每小时峰值雨量小一些,只有 142.8mm),位于灾难中心的泌阳林庄8月7日一天的降雨量达到1005.4mm,最大 6 小时降雨量更达到 830.1mm。有兴趣的可以自行搜索。

这种雨沿海大城市也顶不住

这样的大雨,我直观感觉有天然泄洪优势的沿海城市也是顶不住的。比如我们可以看看东京的泄洪能力——只因我在东京待过 5 年,期间几乎没见过积水,所以感觉东京的排水是相当好,就拿出来作个对比。

东京有个叫做首都圈外郭放水路的工程(可以网上搜搜图,围观一下,毕竟也是热门旅游景点),修了 14 年,大概 130 多亿人民币。这个工程的排水能力是每小时可承受最多 50 毫米的降水,并且在人口和建筑稠密的三个城区可承受 65-75mm 的降水。所以光看排水能力,一样顶不住 200mm 的峰值降雨。即使算上 67 万立方米的总贮水量,我算下来也是不够的,而且差距非常之大。不过如果峰值小一些,全天的降水倒还是真的可以顶住……

烟花来了,杭州怎么顶

烟花带来的降水与前面提到的史诗级灾难比起来并不算大,但听说萧山区已经有些地方被水淹没、不知所措了。

我在网上并没有查阅到关于萧山的新闻,倒是搜到了一票为杭州市防风防汛歌功颂德的新闻,比如

截至7月23日15时,各区、县(市)、管委会绿化管理部门、养护企业已清理断枝植物723株,26.51吨;已支撑树木数量46453株,其中已支撑行道树21545株,扶正倒伏树木23株。

全市共发送雨情预警通告单18份,发送各类预警短信3762条;出动水利技术力量68组634人次,对805处水利工程进行防台检查;全市大中型水库已全部降至汛限水位以下,并通过发电预腾库容;钱塘江沿线闸门开闸预降,萧绍平原和上塘河开始预排,累计预排水量1168万立方米,上塘河水位已由3.30米已降至2.89米;钱塘江沿线闸泵共发布调度指令8条,执行调度预排预泄排涝43台时,预排水量1132万立方米。

部分“老牌”积水点,也是各地各部门提前布防的重点。记者从杭州市城管局了解到,目前,该局已先后组织开展5轮汛前隐患大排查大整治,主要围绕排水设施、城市道路、户外广告、下穿桥涵、河道围堰等重点设施、部位,以及钱江新城、天目山路沿线等重点区域。累计清疏管网长度达7436公里,排查发现的风险隐患均已完成工程整改或落实应急处置“一点一方案”。

感觉杭州可以啊。于是又反手搜了搜郑州的,似乎没啥相关新闻,只搜到了说让停课停工却不听话的新闻。不知是杭州风险意识和宣传双到位,还是郑州确实菜,又或者是单纯地被后续新闻淹没了。不过有下面的讨论,评论区挺尖锐的。

这次郑州的大雨应急工作,对照本人既往经历,感觉存在不少问题,但这个问题不是懒政、不作为或者乱作为的问题,是应急经验欠缺的问题……

我随后望了望窗外,西兴这小破地方百来米的马路上有30来块井盖(虽然大部分应该是燃气光纤电缆相关的),顿时感觉滨江区可能也没那么糟糕。

浙江滚出中国

查着台风与洪水的话题,最后还是来到了 2013 年宁波余姚的水灾事件。这事情 ZF 处理得不是很好,像黑历史一样,但我觉得应该也是真的。

2013年宁波水灾是2013年10月发生在浙江宁波的一起由台风引发暴雨造成的严重水灾。水灾中,甬江流域遭遇有水文记录以来最大规模的降雨,加之天文大潮的影响,宁波多地发生严重内涝,交通、电力长时间大面积中断,其中尤以余姚市发生的灾害最为严重。据统计,宁波全市遭受经济损失超过333亿元,受灾人口达到248万人

当时余姚过程降水量达到 496.4mm,其中张公岭站达到 809mm,也是巨大,本应得到救助与同情。但因入驻的央视记者粉饰太平,在重灾区的路上睁眼说瞎话说没洪水了,直接被愤怒的市民围堵了,也有说被掀翻的。我只能说活该吧,没缺胳膊少腿不是挺好的?下面是看到的一两段网友描述,我觉得应该是真的吧,真收了 5 美分的话也不至于在冷门帖子下面讨论:

我就是余姚的,当时灾情真的很严重,经济损失很大,很多天城区、城镇交通都是瘫痪的,电视台也只有浙江台和宁波台真实的播放了水灾情况,央视新闻也是过了一段时间才轻描淡写的播了一下而已,热搜挂着余姚滚出中国,浙江滚出中国,网友还说浙江人有钱,可以自救,这些都是事实。从那个时候开始,我宁可把钱丢水里,也不往外捐了…

那时候我家还在余姚老城区,整整一个星期,断电断水,楼下积水淹到腰,在水里泡了好几个钟头走路去把家里的老人接过来,一家人靠一个收音机获取外界信息,慈溪人开着大卡车过来给我们送蜡烛食物和充电宝

哈哈哈,我还有照片…什么毛宏芳滚回奉化种田…市政府门口的为人民服务牌子都被人拆了。发洪水当天晚上我收留了一帮外地人,身边将近 40 万现金,提心吊胆一晚上,生怕他妈的被人抢了,当时已经做好准备了,真的要抢就让他们抢,钱是国家的,命是自己的。我还免费供应了矿泉水,面包,红牛。也算是出了把力。军队的话,我说说我看到的吧,南京军区的部队开进来了,当时我对面是梨洲街道派出所,那里有个救灾物资发放点,当时就发了一点点物资,(几箱矿泉水,还没我发的多)拍了个照部队就转移了,亲眼所见,所言非虚。

再然后呀,热搜就有了“浙江滚出中国”这一条,还占了一天的头条位置。

那次的事件好像处理得非常不好,似乎余姚没太收到外界捐赠,之后官方也不太组织捐款。

捐款捐出一堆白眼狼呗,还好从那之后就再无官方捐款

这两年知道2013年的事情才想起来似乎有好多年学校没有组织捐款了….

余姚水灾之后,浙江给全国的捐款从此少了很多。那些老乡省份人士目的达到了

这倒是真的,那两年之后就再没捐过款了,之前不捐还要被老师拉办公室谈话

我只想说,世界真大……真的清一色全是情绪极强的话,感觉那个市的市民受到了成吨的伤害

从那以后心彻底寒了,甚至看见一些外省贫困地区的儿童午餐留守儿童啊那些捐款的活动,我觉得他们可怜可是我一点都不同情他们,也不会再捐款。还有我出去买东西和淘宝购物也尽量挑发货地浙江的。我曾经有过爱心,会帮助别人,但是余姚事情让我认清楚了不是每个你帮助过的人都会在你困难的时候伸出援手,他可能会对你说:“活该”。

真的我记得我小学的时候我们学校常常组织捐钱给贫困山区 包括其他地方发生了天灾 我们也捐 直到那一年隔壁的余姚台风很大 发生了水灾 印象中记得老师眼眶微红说:竟然没一个地方捐款…

余姚那事真的是心寒了,以前别的省有事,每次都捐款,这次自己省有事,其他省让我们滚出中国!受灾那么严重,除了省内新闻,央视都轻描淡写而过,心寒

看看户口,现在我也是浙江集体户,属于人下人了。不小心搬过去的。

最后说点别的,关于浙江与杭州

浙江省确实挺逗的,经济不错,但(建国后)高校就有点难堪。说好听点,全省最差的 211 是浙江大学,全省最差的 985 也是浙江大学。浙江省高校就这,但院士还挺多,全国院士最多的省份是江苏,有两院院士 477人,第二就是浙江,有两院院士 407 人,第三名山东已经不到 200 人了。(不过这也就随口说说,毕竟院士里也有一部分专业水平比较菜的。)

我对杭州的印象挺好的。

从政府方面来看,很多政务服务可以网上办理,去当面办理时态度也比较好。政府的标语是“最多跑一次”,而且办理速度还很快,我的租赁补贴申请后,两三天就通过且拿到当月的补贴了。滨江政府的楼和图书馆(内部)等也都挺好看的。

杭州市的地铁修得挺快的,赶上了亚运会的快车道,已通车的已接近 10 条线,共规划 16 条。杭州市很多地铁站都有自己独特的吊顶设计,我不知道是不是每个站都不一样,但我上下车和换乘的每个站好像都不一样(主要是 1 号线,4 号线,5 号线等),感觉挺好。另外有些地铁站无论是站内(闸机内)还是站外都有厕所,这一点好上加好。(南京相反,很多地铁站没厕所,而且没有修缮计划。我真的看不到“徽京”的前途。)

杭州市的主要交通枢纽杭州东站设计得也挺好的,与地铁接续较快,厕所多而干净。可能大家不知道杭州东站的客流量,它其实客流量巨大,峰值在全国第二、第三的水平。关于杭州东站功能性的设计以及如何能让各路神仙列车入站,可以看这一篇知乎文章。(我研究杭州东站,一开始只是好奇“南京南的厕所这么脏(脏到有点无法忍受),杭州东的厕所这么干净,是不是单纯因为客流量不同呀。”结果却令人失望:如果相关,那也是负相关。)另外还得知了去上海的超导磁悬浮去年又提上了日程,到时候去上海城际只需 25 分钟。

杭州地铁、高铁、机场、景区等的告示牌与导引信息较为充足——与上海比起来,(当年)上海的虹桥火车站和虹桥机场差点把我气疯。举例来说,地铁站里能看到站外公交信息。而虹桥给我印象很深的一件事是,闸机都是三杆子滚动的,大行李箱过不去;以及我过了机场安检在电子显示器上看到的还是高铁发车车次信息,我愣是找不到在哪个柜台值机的信息。感觉那会儿上海俩机场加一个高铁站经常让我感到崩溃,我甚至到了见一个人就要吐槽一次的地步。(我很久没去过上海了,不知是否有好转。)

杭州主城区小,商圈比较密集,逛街较为省心。

杭州一些新修的冷门博物馆也很有意思,除了展览外,还有亲子互动区和体感游戏等。展览也不仅只有功能性,比如我认为其展台从设计上来说颇具美感,它的配色、打光、形式还是挺值得学习的。甚至有些展台还自带布景和音效的。

杭州是真的富,之前一次去安徽绩溪旅行,返程时因大雪和低温导致道路结冰,公交不通,只有当地“民营”的接驳车,而从绩溪开往临安的路,就如同从地狱通往天堂的路,也算是见识了……绩溪那边的路况堪称地狱级别,因为低温冰封,道路很窄;而山谷间还在开路和拓宽车道,所以能使用的车道就更窄了。而开路和拓宽车道的工程,导致路上坑坑洼洼、大小碎石堆积,行车颠簸不已。有时遇到施工工程车,又是黄土漫天。偶尔会通过沿道路两旁建设小屋的小镇,可以看到开着的小商铺,人们在太阳下下棋、打牌、闲聊等,也有的会去凝视经过的汽车。不知为何,那里有点(游戏里的)中东战地的感觉……然后开着开着,猛然上了柏油马路,马路上的白线黄线像新刷上去的一般,格外清晰;两旁的楼也变得错落有致,房顶颜色不一……感叹:贫富差距真的大。再看看 2018 年中国财政税收上缴数据,只有 8 个省是缴了钱的,其他省都在领钱。

总体来说,我挺看好这个城市的,它的基建和一些现在不很引人注意的地方,十年、二十年后将会发挥价值。除了夏天常年 45-50 度的体感热到人要暴毙。

0%