我刚写顺手 CoffeeScript 的时候对程序的理解固然不同,
coffee 当中思路还算清晰, 全局变量和局部变量, 而后有函数,
从而造成大大小小的对象以及闭包, 而后之间的数据发生相互做用,
而这些关联和互做用足够复杂, 能够模拟咱们业务所需的逻辑,
做为脚本语言来讲, 很是灵活的一套方法了.前端
虽然 JavaScript 自己花样挺多, 但 coffee 裁剪的核心很是小,
能够看作是个 good parts 的精简版. 而这些并不足够,
后来 ES6 不断增长功能, 这个事情你们也看到了, 编程语言会很复杂.
而这期间我开始深刻挖 Clojure 方向的技术, 特别是 ClojureScript,
从 React 方向上走, cljs 是很是深思熟虑的语言, 也很天然而然的.数据库
就我而言, cljs 对个人思惟方式产生了巨大的影响.
并且随着我尝试去接触前端以外的一些内容, 想法也也在改变着,
对于不少人来讲, 我如今反思的内容, 也许未曾被疑问过,
对于我本身来讲, 这些思惟上的转变很是重要, 影响个人思考和工做.
固然整理出来, 也更能明确表示我对于 js 和 cljs 的态度.express
使用 coffee 的时期, 一切皆是表达式的观念已经根植在脑海里了,
因为都是表达式, 代码的可组合性很是高, 几乎是任意组合,
好比说 if
在 js 里是 statement, 在 cljs 中是 expression,
那么 cljs 中 if
能够用在代码的任何位置做为参数使用,
就像是 HTML 当中, <div>
的结构能够很是灵活地组合使用.编程
我原觉得 coffee 的表达式已经足够灵活, 但 cljs 还赛过 coffee,
固然这是 Lisp 风格语法的缘由, 全部基于 S 表达式的语言都能作到,
其实在 coffee 当中还有不少缩进的顾虑, 组合会语法麻烦,
而在 js 的 C 风格语法, 甚至在 ts 和 flow 中, 这些问题会更明显,
语言自己的语法制约了其灵活性, 虽然不影响强大, 但总归啰嗦了不少.
我如今以为 S 表达式对于组合能力是至关重要的提高.数据结构
我对 Java 语言不熟悉, 而 js 的面向对象又是花样百出的,
固然我理解的 OOP 显然是有偏颇的, 想法并不许确.
我更愿意接纳 Alan Kay 说的那种基于消息传递的理解,
假设有不少的细胞各自工做, 之间经过信号来协调状态,
这也是整个互联网巨大的生态所展现的形态, 大量的联网的机器,
机器之间经过收发消息来沟通, 从而造成巨大的程序集合体.闭包
然而具体到编程语言当中实现这样的模型, 好比 js, 获就很怪异,
首先代码是单线程执行的, 编程模型其实仍是单线程,
OOP 在 js 中只是将代码进一步结构化了, 算是好维护一点.
而后因为对象实例是 js Object, 能够用 js 代码随意操做,
结果就是对方拿到某个引用, 就能任意修改数据, 这就邪门了.
若是别人的计算机能直接修改你计算机上的数据, 不是乱套了吗.并发
我以为这是编程语言具体实现而带来的错觉, 这不属于 OOP,
OOP 能够帮助分隔职能以便于代码能更好地组织,
可是不必搞成对弈共享内存的不可靠的程度.
固然这可能只在 js 社区早一点的时候比较严重, 如今并不清楚.
当 Clojure 社区批评面向对象是 place oriented Programming,
可能就是批评错了, 那些概念真的属于 OOP 吗, 我很怀疑.异步
其实收发消息的模型更像是并发编程, 而不是单线程,
当你有大量的 goroutine 独立作本身的功能, 这种模式就清晰起来,
每个轻量级进程有本身的内部状态, 而后收发消息:async
Do not communicate by sharing memory;
instead, share memory by communicating.编程语言
这样也就避开了直接拿到引用去别改别人的数据的问题.
并且也更天然, 就像 HTTP 服务发送的字符串数据同样.
消息就是不可变的, 若是不同, 那就是一份新的数据了.
而这样的机制也保证了巨大的互联网可以正常地运转.
说到并发编程, 我大体以为应该分红两种, 好比两个进程之间,
一种方式是两个进程相互有依赖, 要等到对方的行为,
另外一种方式是二者基本上无关, 一块儿启动就行了.
第一种, 也就是进程之间相关依赖的状况, 是我很关心的,
而 Go 的 CSP 模型, 当中的 channel, 就致力于解决这类问题.
有多个进程, 他们之间须要相互协做, 经常要等待, 那就用管道.
那我认为的编程语言两个功能, 一个是模拟, 一个是计算,
真实的物理世界, 或者说具体的业务, 有巨大的复杂性,
当你要用编程语言解决问题, 首先语言应该有足够的灵活性去描述问题,
而后是计算, 比说你能描述字符串文件, 也能描述 zip 文件,
那两种形态之间的转化过程, 语言就要能进行拆解由 CPU 完成计算.
更重要的例子固然是多个任务之间相互协做, 须要能模拟和计算.
纯数据固然足够明确了, 还有讨论一下可变状态和时间的问题,
因为内存和磁盘是可变的, 其实编程语言内建就有可变状态,
只是说从 Clojure 和 Haskell 的角度, 直接这么作是容易失控的,
因此抽象出了 reference 的概念, 值不能够修改, 但能够修改引用.
时间指的是异步任务的等待, 或者是事件流这种状况.
我以为是说, 编程语言应该给出对应的明确的抽象, 来讲明它们是什么?
而后才有清晰的方案说遇到这种状况怎么处理.
js 做为脚本语言而生, Java 和 C# 已解决的问题它却没有解决.
而如今 js 又忙不迭地要加上这些那些功能..
这种作法在 Clojure 看来真的是太混乱了, 想到什么加什么.
我不以为修补问题是坏事, 只是说很难避免不少次生的问题,
好比说社区大量风格不一致的类库, 难以轻松使用.
本来但愿语言自己作好模拟和计算, 结果光是模拟就费好大的劲.
因为 Flux 的缘由, 咱们前端开始关心 Single Source of Truth,
数据是怎么来的, 最原始的形态是怎样, 如何分割?
若是你拿到一个数据 1
, 你固然也能够说这就是 1
, 毫无疑问,
但若是是一个不断变化的数字, 或者说 React 应用的 Store, 就不简单了,
这个 Store 当前的内容并不是 Source, 而是一个结果,
是一个出事状态和每一个后续的操做, 最终造成的结果,
就像是如今的 Git 仓库, 是从空仓库加上所有的 commits 才获得的.
这个问题到了数据库, 以及作备份和同步策略的时候更加明显,
而每一个原子性的操做成了 Source, 才是最真实最小的单元.
可变状态是什么? 就是这些原子性的操做进行计算的中间状态,
于是当面对一个数据库的数据时, 数据库能够认为是可变状态,
而实际上数据库是这些原子性操做的集合, 而且随着时间改变.
回到程序当中的局部变量, 实质上也是这样, 一些操做在时间上的集合.
基于这样的角度, 时间这种外部条件变化, 程序的可靠性就存疑了.
那么说到时间, js 社区近些年才开始集中精力去解决,
好比说开头的 Promise, 而后是 Generator 和 async 函数, 以及 RxJS.
而我会说 CSP, 也就是 Go 的 channel, 就是前几篇文章的内容.
咱们要模拟这个世界当中的具体业务, 离不开时间因素,
所以对于时间相关的代码的抽象也就成了至关有份量的工做.
那我就以为, 能给出基础的操做时间的方案, 那应该是最可行的.
好比 CSP 当中有 timeout
, 在 put!
和 take!
过程都有 wait 机制,
经过这些函数, 或者说指令, 时间成了编程语言执行的一部分,
js 须要用回调解决掉问题, 这里显得比较清晰了, 也就是等待.
还有 alts!
之类的更强大的机制, 能作更多的控制.
也能够说 Future, Promise, async, Reactive Programming, 也是办法,
那个人意思就是我认为 CSP 是其中最为明确和天然的方案.
Rx 固然很强大, 但那是类库, 像是黑盒, 而不是单纯语言提供的抽象.
C 风格的语言每每从硬件角度, 提供 mutable 的数据结构,
而 Clojure 跟 Haskell 当中, 这一点是很是谨慎的,
Clojure 认为数据就是数据, 怎么能随意更改数据, 只能更改引用,
对应到真实世界, 苹果就是苹果, 怎么可能变成梨?
只会发生的是, 盒子里原来放苹果, 如今放梨, 关系随着时间改变了.
因此这才是更准确的用代码模拟世界的方式, 苹果不能变梨.
一样地, 当代码复杂到跨越大量的机器, 在不一样的时间节交换状态,事情自己是会愈来愈复杂的, 无法避免, 编程语言仍是要模拟和运算,但是咱们有机会找到更准确的概念去描述他们, 并且更准确,单纯的脚本语言表达能力固然不够, 结果就须要不断增长新的概念,好比说造一个些类库和语法, 加一些新的概念, 说能解决这个问题,问题在于, 这些概念自己也可能过于复杂, 超出文本自己的复杂.这种时候某些语言表达能力足够强, 把问题弄透彻了, 那就赞了.