我之前的背景主要是 js 和 ClojureScript, 对类型了解很有限,
到 Nim 算是才开始长时间使用静态类型语言吧. TypeScript 那只当 type checker.
Nim 的明显问题
JavaScript 到底是 Google 砸钱了的, 调试的体验真的是好.
至于 Nim, 大部分的报错靠着类型信息倒是也能定位出来,
不过没有趁手的断点调试工具, 经常要靠大量的 log, 我也挖得不够深.
VS Code 使用体验自然也远远不能跟 TypeScript 比, 我直接 Sublime 了.
Nim 的 echo 首先就很让我头疼, 没有自动加空格, 挺烦的.
Nim 类型和运行时的一致性
TypeScript 虽然有很风骚的类型系统, 但我也没用着 strict, 也不是所有依赖都 ts.
然后偶尔会遇到写了类型但是底下完全不是那么回事, 就很莫名.
Nim 的类型跟数据是直接对应的, 使用当中除了一些 edge case, 都能对应上,
意味着类型检查报错的地方修复, 代码对应的报错也就解决了,
这让我感觉到类型才是可靠的. 当然, 很多静态语言应该就是这样子.
Method call syntax
Nim 不是面向对象语言, 里边的 object 大致对应 C 的 struct, 而不是对象.
object 里边就是数据, 这个还是比较习惯的.
不过代码观感上, Nim 还是有点贴近 js 这样支持 OOP 的写法的,
我是说大量的 a.b(c)
这样方法调用的语法, Nim 里边叫做 Method call syntax.
也刚知道这在 D 里边已经有了, Wiki 上都明确说了:
https://en.wikipedia.org/wiki...
这个特性对应的 Nim 代码是这样子的:
type Vector = tuple[x, y: int]
proc add(a, b: Vector): Vector =
(a.x + b.x, a.y + b.y)
let
v1 = (x: -1, y: 4)
v2 = (x: 5, y: -2)
v3 = add(v1, v2)
v4 = v1.add(v2)
v5 = v1.add(v2).add(v1)
最初我使用的时候没有在意, 但是随着迁移一些代码到 ts, 才感受到灵活.
在 JavaScript 当中, 继承, 多态, 依赖 class 结构才能实现,
这也意味着我要定义 class, 然后 new instance, 然后才能用上.
但定义了 class 也就意味着这份数据不是 JSON 那样直白的数据了.
我对 OOP 使用不多, 但是思来想去大致也理解, 动态类型能做到 JavaScript 这样已经很强了.
Nim 的多态是通过编译器判断类型来实现的, 比如前面的 add
可以有很多个 add
,
proc add(x: Y, y: Y): Z =
discard
proc add(x: P, y: R): Q =
discard
后边遇到 o.add(j, k)
根据类型在编译时候就能实现多态了.
当然这在 JavaScript 靠 class 是能够实现的, 但那就一定要把数据操作绑在一起了.
长期使用受 Scheme 影响的语言, 对 class 这个臃肿的做法就很难适应.
有类型的情况下, 在这套方案当中 overloading 很自然的,
比如 Nim 当中对类型 A
定义 equality 判断的写法这这样的,
type A = object
x: number
proc `==`(x, y: A): bool =
discard
没有耦合在一起, 意味着我引用 A
类型在另一个项目也能自行扩展,
而且这基于类型的, 不会修改到原始的模块当中, 不影响到其他引用 A 的代码.
这一点, 我的代码从 Nim 转译到 TypeScript 就比较头疼,
因为我定义数据结构的访问和判断需要 overload 这些个 array accessing 和 equality,
我一时半会也想不出来 TypeScript 当中能怎么做, 只能用 mutable data 在运行时强行模拟.
没有碰过 Java 跟 C#, 碰过语言当中这套玩法跟 Haskell 倒是挺像的,
Haskell 从 class 产生 instance 的时候可以定义一些函数, 就很灵活.
(具体 Haskell type class 高阶玩法真是还玩不起来.)
不过 Nim 相比来说, 简化是简化了, 但这个语法糖在编码当中就是很方便.
也因为缺失这个功能, 导致我对 Clojure 跟 TypeScript 这都有点不是应该了.
动态数据的类型
转译代码还发现的问题是由于 JSON 跟 EDN 极为便利,
引起我在大量代码当中直接使用 Array 和 Map 直接表示数据,
这个不能算错, 但数据在 Nim 当中这些都是明确用类型表示的,
也意味着在 Nim 当中有明确的结构进行判断, explicitly...
反观我的 TypeScript 代码, 大量的 Array.isArray
,
然后还有那个不知道怎么说的 typeof any === 'object'
的尴尬,
在类型系统当中使用习惯之后, 回过头感觉特别不踏实,
然后我自动跑去折腾 instanceof
的玩法来做对应功能了.
当然, JSON 或者 EDN 通用地表示各种数据, 确实在通用型来说非常好,
我跨项目调用数据, 这些动态类型就是直接通用的结构,
在 Nim 当中, 一般传递数据是会涉及到一些类型转换, 写写是有点麻烦的.
我不是很能衡量那种方案是更好, 但是对于底层类库, 我是希望有明确类型的.
其他
TODO