coffee-js / languages

编程语言学习论坛
https://github.com/coffee-js/languages/issues
112 stars 11 forks source link

面向对象一些遐想 #57

Open tiye opened 11 years ago

tiye commented 11 years ago

少有时间梳理一些头绪出来, 自己去想过的事情, 总会安心很多 我想的也是我猜的, 至于对错, 哪里知道 毕竟我是写 JS 的, 基于原型的面向对象, 思路也是从 JS 走的

抽象是关键

无论函数式编程, 面向对象, 对于抽象都是极为重视的 抽象是复杂问题逐步被简化的关键, 函数式抽象函数, 面向对象抽象对象 抽象存在于各种层次, 两者分别用函数用对象来模拟 编程到底是对人类对现实世界的理解的模拟, 语言也是模拟

现在回想, 面向对象的理念封装和接口觉得全都是对的 大概实现起来太烦人, 而且僵硬程度和代码长度都让有些人反感 很像图形界面和命令行之争, 图形界面有很好的功能, 现实确实不好的

面向对象对现实的模拟非常符合人们的理解 一本书有其属性, 一个书店有其属性方法, 包括对书进行操作的方法 看一件事物也有他的属性怎么样, 有什么样的方法和操作 相对来说我更喜欢事件或者消息的称呼, 方法很有术语的感觉

'域' 是个非常直观的概念

编程语言里域的概念很清晰, 变量有对应的域, 函数内部有域 现实 世界中域也经常能抽象出来, 同一的位置, 同一的时间段 JSON 的结构中, 哪个数据绑定在哪个域, 这个非常好理解

域的概念很容易有嵌套的形式出现, 且不妨碍人们理解 而且域之间关系很简单, 域内部知道自身的数据, 知道外层的域包含的数据

JS 数据的索引是通过原型链进行的, 而原型链随着赋值会发生改变 但我想, 引用和绑定并不是一致的概念, 像文件系统的文件和链接 一条数据, 所属的域发生一次引用, 并不代表数据所在的域改变了 那么我想赋值在 JS 里是有问题的一个概念

暴露的接口

将数据抽象为域, 就需要提供域和域之间通讯的方式 简单说是属性的访问和方法的调用, 我觉得用事件作为关联会更好 而被传递的, 无非是一些数据和域的引用 方法的概念, 觉得明显是调用的意思, 并不通用, 我换成消息来说 一个域接收到一个消息, 然后知道做什么, 而不是说进行一个方法操作

一个域为了更好地抽象, 屏蔽复杂度, 封装和隐藏私有变量必不可少 这在模块化里自然而然, 一个文件被调用时, export 出的数据只有一部分 但在 JS 中, 对象的方法属性是直接暴露, 就没有操作的手段 我期待在编程语言中能自然而然给任一个域声明哪些能被访问的消息

一个域有良好的接口, 好比拿到一个函数一个终端命令去找怎么用 首先, 一个如同 help 的接口, 给出所有能调用的方法以及文档是更好的 也因为想到对暴露的消息, 包括输出的消息做额外声明, 可以选做文档 甚至于变量名和代码片段都是好的文档书写方式

比如一个例子(后边这部分我写得太过随意了..):

scope =
  a : "a"
  b : "b"
  @ "display"  | c |
    % "print" (return a "concat" c)

就比如这样一个, 暴露事件 display, 调用时触发 print 事件 而 @ 以后的很多内容其实作为文档也比较清晰了

函数式语言的状态

相对于消息, 函数的概念显得独特, 同时函数包含了太多概念 JS 里函数无一例外创建了自己的作用域, 有些语言还强制返回值 相对来说一个消息仅仅是接受一个消息触发一系列操作而已 函数似乎可以拆分成多个正交的操作, 在对应场合使用

函数和面向对象一样都要面对状态, 只是函数式编程方式独特 reduce 的例子, 有初值, 有数据的列表, 有数据合并的方法 其状态一直在参数里不断被传递, 并在起始和结果里有给出 昨天看的视频里管列表中每个数据叫事件, 这实在是形象

数据结构

数据和操作往往是绑定的, 函数和数据类型也常常就是一一对应 个人觉得绑定了更舒服, 且按照前边的想法, 它们是在一个域 换种角度, 一个数据, 接收某些消息, 做某些事情, 自然而然的

在 JS 里, 数组也是对象, 只是数组有 push 这类方法 而数据存储在 0 1 2... 这些位置上, 还是一个对象的样子 数组内部因为 this 的关联而成为一个域

模块相互调用的方法

域的概念, 方便于内部快速方法, 而域之间被隔断, 而通过消息来通讯 JS 里典型的手法是 onemit, 监听和触发事件 程序里发生的事情, 或者我们可以理解为对象间消息通讯协调任务的过程 Smalltalk 那时就提出来了, 只是那么些文档真心弄不懂

比如现在我给出一段伪代码, 尝试用消息去理解:

token : Foo <- "calling" {data: 'x'}
result : token :: "return" {data: "y"}
Screen <- "display" {data: result}
Screen <- "display" {data: "nothing"}

怎么理解, 取决于怎么定义语法, 是否对代码进行阻塞

  1. 存在阻塞: token 保存一个引用, 保存 Foo 发生 calling 的返回值, 赋值到 result, 最终顺序打印, 有 result 的内容, 还有字符串 nothing
  2. 回调: token 保存引用, 等待事件返回的内容, nothing 被先打印出来 之后事件回调, 写入 result, 而依赖 result 的调用也终于执行了, 打印
  3. 乱序执行: 按书写的顺序全执行一遍, 打印的 result 还是空的数据, token 只是接收到回应 函数的 return 是个很优雅的方案, 可真的是扎实存在的么?

    继承和多态

不同的对象, 共用的方法和属性用多态和继承, 在 JS 的原型似乎做不到 但不同的域, 可能存在的消息, 而这也应该被抽象出来 可这细想和编程语言如何实现关系不小, 想不清楚... 我想总是可以改进域的形态, 简单地容纳这样的抽象存在 比如域可以直接用外层的域的消息, 这样就以某种形式达成了

模块的组合

大的程序拆分成各个小的模块,如何精巧地组合模块成了大事 直观的理解, 就是怎样发送消息, 怎样让回复的消息和发送的消息对应 另一方面, 怎样能更准确地索引到需要索引的域发起消息 或许面向对象各种讨论这些问题, 只是我不知道

模块组合之后, 还有调试模块间消息的通讯的过程是否正确 打惯断点以后, 对这样一种将执行过程可视化的方案非常认同 即便将函数的概念偷换成为消息, 也只是换了一种形式同样地查看运行过程

函数复合

语言里写的消息, 一个不足就是函数式编程坚守的返回值和复合功能 因为函数能不断复合, 更精炼优雅的程序就能被写出来, 简洁有力 消息未必有返回值, 但为了代码复合的方便, 也许可以制造一些返回值 比如接收消息后, 返回一个接着能接收消息的管道, 再继续后边的操作

小脚本

为小的脚本去写整个对象不合算, 因为简单的胶水语言就能把事情完成了 换个角度想, 其实一个文件就是一个域, 代码其实都有一个域 文件默认的域作为可以省略的语法, 那么通常代码并不缺少一个域

ghost commented 11 years ago

现有所有编程语言错误的是:'域'(上下文)是 buildin 的。

'域'成了系统的一部份,成了固定的知识。没有形式化,也就是“不可编程的”。

所以闭包(常见的'域')是错误的。它将几种不同概念混在了一起。

需要的就是将隐藏的'域'(上下文)重新显式的形式化成可编程的 code。

重写可能也是如此,系统的未形式化的 buildin 部分都是问题之源。

tiye commented 11 years ago

@emerge 看到你说"错误"我还是吓了一跳,, 看后面的解释我还是同意的 最近看得东西让我越发肯定 JS 对自身的作用域还有执行过程的控制能力太弱