把函数语言当编译原理用,在上面打造命令式
本文关键字:语言的方言系统,范型,编译原理,自带ast支持的语言,把函数语言当编译原理用,把语言虚拟机runtime当os用
拿稍微现代一些的流行编程语言和派系来讲,命令式和函数式语言几乎主导了现代语言runtime和语法设计的各半边江山,前者以py,ruby这种语言为代表,在运行时里内置了udt/adt作为第一类对象,甚至整型这些primitivetype都弄成了adt,这是这类现代命令式语言运行时和语法设计的关键思路。----- 用它们进行编程,基本动作,就是对运行期内存对象树各种抽象数据结构“对象”进行编排,命令作为代码结构,发展出面向对象基于对象基于原型链这些范式。
后者以erlang,Elixir,js(当然,ruby,js,py都是混合过程+函数范型语言)为代表,它们以函数同时作为代码结构和数据结构,并演化成语言和库层面的可用结构。发展出函数范式。
以上这些在《lua/js/py复杂度分析,及terralang:一种最容易和最小的“双核”应用开发语言》和《why elmlang:最简最安全的full ola stack的终身webappdev语言选型》,以及《我为什么选择rust》,都有涉及。
这就是说,具体编程就是一种在具体语言实现下,组织使用数据结构和代码结构的活动(冯氏中数据和代码本来就可以相互转化)的过程,编程语言对于这部分都会给于满足,而具体编程活动还总是一种使用语言和扩展语言相结合的过程,这种“扩展语言本身”的动作,有时,不被注意到。比如”发明库就是语言的扩展“,也是扩展语言的一种,跟普通编程无异,最初级的程序员也能办到。还比如,定制对象体系,标准库,甚至语言语法/语法糖。这往往是语言设计完后之后的使用层面的东西。
甚至它们被做成了框架,框架即是一种对从语言语法增加到业务逻辑所有问题打包,直到app模型产出的”一次设计“,
这就是上面提到的具体一门语言之后的东西,比如语法增强,应用标准语义化成sql,xml,html这种。
而有些场合场景,并不总是满足于这些语言之后的东西,,需要专门的dsl手段。要求语言提供更多的dsl和扩展语言本身的手段。,而这些高级的dsl机制,,有时还得从根本上修改语言的底层行为。比如泛型,运行时pme,内省,接口(这里指借助语言内集成编译类和服务不必涉及到编译原理也能完成的一类动作,而不是发明一种独立语言的意思)。甚至在语言层面集成创建dsl的编译设施器。甚至为这种语言制造方言系统和可再发行版,
对于dsl的支持,并不是每种语言都能给出很好的支持。支持的方法和范畴也极不一样。dsl定制语言选项对于具体语言实现毕竟是个可选项,有一些语言根本就不带,有一些语言从设计级别就自带dsl。
不妨把这些所有,统统称为语言对于dsl的支持。
Code generation:语言内dsl设计的套路,及如何简化
这里谈Code generation:这种最典型的语言级别扩展的东西,事实上,现代语言对于Code generation的支持是很丰富和复杂的(有Macros,Eval,Reflection,Introspection,Intercession,Modification,intercession)。泛型最初被实现的典泛并不是cpp,而是函数语言。范型并不是一种奢侈的语言机制,而是一种重要的扩展机制。参数化类型可以在编译期做也可以在运行期做(解释语言重运行期,只能用内省,pme这种达成),只不过cpp的模板将它做得越来越复杂,让我们误认为它是种高级的东西,实际上,它跟c的宏一样重要而基础。
不信?历史上多种语言对如何范型化,有不同的做法和标准。见《A brief introduction to Metaprogramming in JavaScript》
这里的问题在于:现代的过程式语言,它们包含了太多概念(单单一个js就涉及到以上大部分),它们都太复杂了,具有深重的历史包袱。它们利用到的概念不够简单和正交,使得人们学习和使用它们的过程极其漫长和艰难。
我们来看语言外dsl化的那些手段
语言外dsl化的套路,新语言设计和扩展的套路,及如何简化
语言外部dsl化,主要是另行起工程的:
1,平台runtime:提出很多app runtime和语言级es标准,比如js有node一套,browser一套,这其实也算是再造语言的方式
2,还有语言方面的runtime:clr/llvm/webasembly,提出很多 强化了的host runtime,这种先于语言出现或后于语言出现设计的runtime和标准中间转化形式。为jit和语言间的转化,甚至编译到本地平台,提供了一种可能和工具链打包为其它平台的机制
llvm的作用,跟wasm一样。主要是面向后端的。给运行时设置一个统一指令和vm后端。方便统一前端的语言向它转化或向它转译。
llvm的父子项目clang只是它的一个应用场景。甚至可以在clr中应用llvm,llvm也不限于对本地生成可执行码
3,语言变体方面的:为一门解释语言提出一个restrict版本的中间语言,限制类型的动态能力,方便以其为中心打造一个新语言,比如js有ts,perl有rperl,python有rpy。terralang有terra。把整个中间语言当编译语言/jit语言/静态类型语言使的translator toee机制。2,3往往结合,与2那些一个语言一个专用runtime的方式不同,这些为一个平台上的多语言提供一个通用runtime,且面向“多语言转译目的”和“转化到中间层表示”。这种为了转化成本地码,而把脚本语言r化。进而提速的路子很普遍。
比如rpy之于perl,但它不是为了像rpy一样发明工具链和语言,而是为了生成native exe,也没有jit,光是为生成native exe速度提升实现,它里面有三种数据结构和操作,如果是cpp的那种。
说到这里,我觉得脚本语言打包成平台二进制或vm可执行体才是正常的类似go。源码应该仅限于开发时调试用此时运行速度慢的脚本没有jit都没有关系,但一旦经过发行,必须打包成平台码和jit码。开发时脚本作为设计源文件存在,而不应发行出来,比如js就没有webassamly科学,不妨进一步Compiling Javascript to wasm,
这里的问题也在于:现代的过程式语言和语言化的这些dsl手段,它们太复杂了,而且繁复曲折(为了解决新问题不但创造新问题,不好终身使用),里面的概念太多,具有厚重的历史包袱,它们往往,都是编译原理级这些机制。用发明基础设施的手段来实现dsl。
结合上面,对现代过程语言的dsl化,已到一个绝路,我们希望抛弃所有这些:获得一个用简单有限正交概念集就能完成,平直学习典线,又不失流行功能的包含语言内外dsl的新语言体系。
这就是函数语言。和基于函数语言的新思路:把函数语言当编译原理用,把语言虚拟机runtime当os用。
函数语言:真正的方言系统,把函数语言当编译原理用,把语言虚拟机runtime当os用,把管道和bash作为最上面命令式语言,一门语言一门os
首先,把函数语言当编译原理用,拿Elixir为例,可以这么说,Elixir 是披了现代编程语言外衣的 Erlang(只是可惜它不是过程式的,与它有同机制类似的ruby也不是),Elixir在此基础上做了一件也许是跨时代的事情:支持macro,它把ast作为第一等公民,内置入语言。我们可以为他写库,写成dsl和方言。把函数语言和runtime作为中间品,在上面打造我们常见的命令式。而不是透露函数或使函数式和命令式共存,而是把函数式作为过程式的基础,属上下结构而不是平行结构。掩盖函数式,函数式只作为被转化目标和底层机制
其次,把语言虚拟机runtime当os用,实际上这个思想在《rcore,zcore,兼谈fuchsia:一种快速编程教学系统和rust编程语言快速学习项目》我提到过,建立起os,语言runtime之间的一一对应,可以使开发和os一体,shell即app环境。实际上,这也是有支撑基础的。os和语言runtime间存在几乎一一对应的概念等体,基本上就是彼此再造。比如os进度对应erlang抽象进程(异步的本质是一种并发进制),对象对应抽象对象,文档对应抽象文档
在《一种开发发布合一,语言问题合一的shell programming式应用开发设想》我们还提到管道和积木编程方法,这跟vb的visual trigger一样,是新型的基于过程式上的范式。管道对应我们编程搭积木的方式。如果能在其中发明一种类似《Magritte: A Language for Pipe-Based Programming》的类bash语言。一行式onliner调试easy debug。不用开个ide写class。像bash一样onelinerlang,在日常打开的command shell和日用环境中就能完成所有的开发调试。bash的简单本质就来源于此。
而且可以降低整个工具链层面的复杂度,语言repl作为os shell使用,打造一个环境始终在线的类busybox的ide。
比如用py等语言编程,你得在机器上放语言,包管理工具,放库,放框架,放app runtime,最后弄成甚至类似vscode一套的东西,才能好好开发调试。而如果语言内部的东西也很复杂化的情况下,势必越做越复杂。
而带原生dsl的语言。可以all-in one-core。一个单独的发行就能代替所有这些。包含一切它需要的设施,开箱即用,自包含更轻量。
比如ellie for elmlang几乎是一个足够高可用的全生态ide,甚至作为可作为一个os的shell使用
只是现在这种思路的语言系统还没有出现
(此处不设回复,扫码到微信参与留言,或直接点击到原文)