未来属于声明式编程
声明式编程 (Declarative Programming)是一种编程范式。现实世界中,我们大部分编码都是命令式的。
举个最常见的例子,对于用
// 普通的 DOM API 构建 UI
const div = document.createElement('div')const p = document.createElement('p')p.textContent = 'hello world'const UI = div.append(p)
// React 构建 UIconst h = React.craeteElementconst UI = h('div', null, h('p', null, 'hello world'))
所有的
SELECT * FROM Products WHERE name='Alipay'
声明式编程的潜力在于:
解放人力成本,你只要「声明」你要做什么,具体怎么做,由运行时解决。
函数式编程就是声明式编程的一种,在函数式编程里的尾递归性能,就取决于运行时,而不是靠程序员去手动优化。
我们可以认为
运行时帮你完成工作,除了可以节省人力成本外,还降低了程序员出错的概率 —— 因为写的代码越少,出错的概率就越小。人是最不可靠的,我们应该尽量把工作交给计算机。
「声明」是「描述」而不是真正「执行」
在纯函数式编程语言里面,一切都是声明式的,是纯
putStrLn
)就是一种副作用。在putStrLn "Hello World"
本身不会真正地输出 “Hello World“
Elm 和

以上说的这些,可能太过抽象。所以我用前端的同学们应该都知道的
下面我将用 官方文档的例子 做解释。
比如,以下是一个有副作用的函数
import { call } from 'redux-saga/effects'
function* fetchProducts() { const products = yield call(Api.fetch, '/products') // ...}
显然,Api.fetch()
是副作用,它会发送网络请求。但是,在call
告诉Api.fetch
/products
所以,事实上这个函数没有被命令式地被执行,而是由
如果你在外部直接调用fetchProducts()
next()
得到你
const iterator = fetchProducts()
// expects a call instructionassert.deepEqual( iterator.next().value, call(Api.fetch, '/products'), "fetchProducts should yield an Effect call(Api.fetch, './products')")
也就是说,你要测试的是「你有没有告诉程序你要执行的副作用,以及执行的参数是什么Api.fetch
,你必须用测试框架里类似 mockFn
的手段去
fetchProducts()
只有在
所以,声明式的编程是非常易于测试的。
可视化编程是一种声明式编程
我们探索可视化编程,是因为我们一直期望通过拖拽就能完成开发,其实就是期望我们完成任务仅仅需要通过声明,而不是写命令式的代码。当然这是一种理想的状态。
query { posts { id, title, content }}
把网络请求变成声明式的好处有很多,其中一个就是它可以被放到各种各样的环境被执行。想象一下,我们可以打造一个可视化的应用搭建工具,在命令式编程的场景下,我们如果要做出如「点击按钮发送请求,得到响应后触发另一个
async function onClickButton() { // 手动发送请求 const result = await fetch('/api') // 手动更新 UI table.dataSource = result}
如果是
- (声明式地)编写
GraphQL 查询语句 - (声明式地)为组件(比如某个按钮)绑定
onClick 事件为触发某条查询语句 - (声明式地)为组件(比如某个表格)绑定某条查询语句的响应值对应哪些组件的属性值
当然现实世界的应用不是那么简单,但已经是跨出了很大一步。
Conclusion
未来为什么属于声明式编程,因为我们在不断地努力提高开发效率,声明式编程显然是提效的最佳手段。
