4

编程语言的类型系统并不是核心问题,不要迷信银弹

 2 years ago
source link: https://acejoy.com/2021/09/06/656/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

编程语言的类型系统并不是核心问题,不要迷信银弹

2021/9/6 0 Comments 54 Views 0 Times

我是从C/C++时代走过来的开发者,对静态语言的那些设计上的设定,非常熟悉。然而,我却发现不少只研究JavaScript的程序员,对程序语言的“数据类型”问题,有着迷之自信。

典型表现就是:他们把TypeScript吹捧的过度了,似乎系统开发没使用TypeScript就是不靠谱的系统;开发者没有使用TypeScript就是不合格的。这个结论是荒谬的。

略微推测下,有几种可能原因:

布道者的吹捧。这些鼓吹有些是自发的,有些则是拿了推广费用的。大公司为了推广某些技术,雇佣、赞助布道者写文章、答疑推广,是很常见的事情。谷歌、微软没少干这个。但是这没什么,技术竞争而已。关键是自己要会分析总结,不要听到就认为正确。

JavaScript的缺陷。它确实是有很多问题的编程语言。虽然此语言演化速度非常的快,可是为了向后兼容性,一些根本性的缺陷是很难修复的。

TypeScript的出现解决了不少JavaScript缺乏类型系统导致的天然不足。不过请注意,以传统的C/C++等编程语言为代表的“静态语言”,和以Python、JavaScript等编程语言为代表的“动态语言”,从设计之初,对类型的考虑就是不同的。

换句话说:动态语言缺乏“类型系统”,是设计而非缺陷!它是故意这样做的。为什么?因为动态语言的开发效率很高

不同设计的语言

C/C++等静态语言开发是很麻烦的,语言上的限制是直接原因。代码从头到尾考虑类型问题是很大的负担,另外一个就是手工内存管理的负担。静态语言以这些为代价,换取了程序的高性能运行。Python为了严谨,故意要求显式的数据转换,但是仍然比静态语言容易的多了。

静态语言的类型在变量声明这边标记,编译时候确定并检查,而实际上动态语言也不是没有类型,它是存储在了变量内容这边。因为动态语言变量指向的数据,可以随时切换,所以才显得“缺乏类型”。这里先不说类型强弱的话题。

看图,一图抵千言。

类型区分动静之别

上图来自:https://prepinsta.com/python/dynamic-typing-vs-static-typing-in-python/

如果你认为,使用静态语言编程序引发的bug较少,这个其实是不符合统计数据的。统计数据表明,代码的缺陷数量,跟语言是否静态关系不大,跟代码量关系比较大。或者说,只要有足够多的代码行,功能复杂,无论用什么语言写,都会出现软件缺陷。使用静态语言编程序,有类型系统支持,确实可以在编译阶段消除一些问题。但是C/C++写的程序Bug就少么?没有的事情,软件缺陷并不会因为强制性的类型约束完全解决。反而,因为动态语言书写方便,开发效率高,各种库众多,模块化程度更高,代码写的比较少了。总体的缺陷实际是可以更少的。

动态语言有个天然的不足,变量类型在运行中,变来变去,导致很难静态推断程序的执行过程,数据流的加工和变化。这也导致,它开发出的系统,性能较差,很多问题在运行阶段才能发现。

TypeScript引入类型系统,为JavaScript增加了“可选”的类型系统,注意:它是可选的。这个类型系统有不少好处,比如:

  • 1、IDE智能提示、智能检验调用关系的参数、结果匹配。如果你使用WebStrom,就会发现它已经这么做了,写错的不匹配的类型,自动标记提示,非常的有用。

2、更严谨的接口设计、数据结果设计。接口定义一下,如果有修改造成不匹配,会在编译阶段就立刻知道,这样就及时消灭了潜在的Bug。

除此以外,选择TypeScript,可以用更新式的、更方便的语法书写程序,可以编译成向后兼容的代码。舒缓运行环境对js版本支持不同引发的问题。

然而,它并不是“银弹”。如果是的话,何必做成“可选”的类型,直接默认强制开“strict – 严格”模式就好了。因为强制的类型会带来开发的负担和兼容性问题,而这点正是需要平衡考虑的事情。如果你认为这个模块可靠性非常重要,那就开严格类型,强制要求。不重要,完全能控制得住,那就直接跳过吧,把调用接口、运行结果控制住,就已经成功了一大半。

开发类型支持是需要大量脑细胞的,不信的话看看:

type Exclude<T, U> = T extends U ? never : T;
type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>;
const InjectColor =
<Props extends { color: string }>(Component: React.ComponentType<Props>): React.ComponentClass<Omit<Props, “color”>> =>
class extends React.Component<Omit<Props, “color”>, any> {
public render() {
return <GetColor>{color => <Component {…this.props} color={color} />}</GetColor>;
}
};

这一坨一坨的泛型,比臭名昭著的C++模板还复杂,你确定这就是使用TypeScript的最大优点么?即便我能理解、有效的使用,团队中的其它人呢?对于软件工程来说,这个风险和成本并不是不用考虑的。这种“类型体操”,多半是危险行为,开发维护成本都很高,并不值得推崇。然而一些TypeScript的铁粉,坚持认为这就是最佳实践,必须把每一处类型都要定义清楚,这才叫“赏心悦目”。迷之自信。连最新版的C++都推荐使用“auto”了,你们怎么还倒退回去了。过犹不及。

对于系统的可靠性,真正重要的是充分的测试、测试、测试。重复三遍

无论是什么语言开发的系统,只要有了充分的测试,特别是自动化的回归测试,有各种数据支持的测试案例支撑,可靠性、稳定性都不会太差。现代应用程序,因为模块化程度比较高,网络操作深度绑定,调用链条又长,所以很容易在模块边界上出现各种问题,而这都需要用有效的测试来保证,并不是语言本身可以解决的。那,测试岂不成了“银弹”?当然不是,测试解决不了系统设计、开发上的缺陷。它只是尽可能的保证,软件的运行结果符合预期。

结论:没有银弹,类型系统不是,TypeScript也不是。认真细致的设计、遵守规范的开发,严密到位的测试,这才是软件质量的重要保证。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK