September 22, 2007

基于消息传递的 Scheme OOP

续上次的OOP 诡异教程,我用 Scheme 宏写了一个类似的系统,拿来晒晒。

;; 这里只有部分定义,快速排序和二分法查找的代码就不帖在这儿了
;; 这里是 slot 类型的定义,Scheme 中不能动态 eval,只能用点别的招数
(define (make-slot name act) (cons name act))
(define (slot-name s) (car s))
(define (slot-act s) (cdr s))
(define (slotstring a) (symbol->string b)))

;; 主要的转换宏,语法比较丰富,但至少要带有一个 that 块
;; 语法可以只给出 that 块:(class (that (slot-name1 slot-act1) ...))
;; 也可以指定继承,要求在 class 关键字后出现原型对象:
  ;;(class extended-obj (that (slot-name1 slot-act1) ...))
;; 在 that 块之后还可以加入 where 块,这样可以指定不对外可见的类属性
;; 需要注意的是,where 块中的定义不能引用 self 等特殊命名,that 才行
;; 另外外部只能引用 origin, has-slot?, slot-names 3个特殊命名
(define-syntax class
(syntax-rules (that where)
 ((_ that-block)
  (class (absobj) that-block))
 ((_ org that-block (where def ...))
  (letrec (def ...) (class (absobj) that-block)))
 ((_ org (that (slot val) ...))
  (lambda ()
   (letrec ((slot val) ...)
   (let* ((origin org)
       (slots (list-qsort slotvector (map slot-name slots)))
       (slot-acts (list->vector (map slot-act slots)))
       (has-slot? (lambda (v)
             (vector-bsearch symbol index -1)
           (vector-ref slot-acts index)
           (case verb
            ('origin origin)
            ('slot-names slot-names)
            ('has-slot? has-slot?)
            (else (origin verb))))))))
    self))))
 ((_ that-block where-block)
  (class (absobj) that-block where-block))))

;; 这里需要注意的是,因为嫌烦,absobj 被实现为了一个空壳子
(define (absobj)
(lambda (verb)
 (display "This object can't handle ")
 (display verb)
 (newline)))

测试一下:
> (define c1 (class (that (x 10))))
; no values returned
> (define c2 (class (c1) (that (z 16) (y 12))))
; no values returned
> (define o2 (c2))
; no values returned
> (o2 'x)
10
> (o2 'non-slot)
This object can't handle non-slot
#{Unspecific}
> (o2 'slot-names)
'#(y z)
> (o2 'origin)
#{Procedure 8647 (self##497 in c1)}

输出好像很古怪的样子~`我用的环境是 Scheme 48 虚拟机。
这儿的例子只覆盖了一小部分,剩下的比如 where 就自己慢慢玩吧。
Scheme 的“卫生宏”不但强大,还能制止你设计不好的语法,有意思。
消息传递真的很有意思。留个小题目,增加一个特殊方法 clone,把继承机制调整为差异继承+纯基于对象,类似 IO 语言 的面向对象机制。
这个“系统”的完整代码在此下载,升级版本就算了吧 :)

September 16, 2007

Mazy i 编程语言定义 (1,2)


 ;; 纯属写着玩儿,尚未最终决定的版本已经与此差别很大,比方说 第二章。
 ;; 因为修改麻烦,本博客有关 Mazy(i) 设计方面的文章从此只发表第一稿。

1. 前言
Mazy i 编程语言,是一种用一阶λ演算的观点解决普通数学问题的纯函数式编程语言,具有极其简洁的语法和一定的通过数学表示推导解决数学问题的能力。它被设计为一种附合熟悉普通数学记法的用户在思考函数式编程时使用的简记记法的语言。它在语法、语义、实现方面的设计必须附合这一要求。第2章对此给出了概述性的解释。
第3章具体描述 Mazy i 的所有语法,每个语法后有用 Mazy i 扩展记法给出的语义描述,某些语法可能附上实现指导。
第4章描述了 Mazy i 程序文件中位于代码之前的文件说明区块,以及程序文件的加载、执行方式。
第5章描述了 Mazy i 内置库中的所有函数和宏,重点概念会给出 Mazy i 扩展记法的描述。
附录A总结第3章给出的用简化的 BNF 表示的 Mazy i 形式语法。
附录B列出了部分参考文献。
Mazy i 指的是 Mazy 语言第三版。Mazy 代表数学和懒惰,不是混乱和忙碌。字母 i 源自 Mazy 语言的版本命名传统,从第一到第七版分别为 s-t-i-n-g-e-r,排版字体为斜体,下标。Stinger 这个单词是作者姓氏,此外没有其它含义。
以下用 Mazy 代指 Mazy i。

2. 概述
编程语言必须对其设计意图有强大的表现力。强大基于完备,完备基于简单。
Mazy 的设计意图是作为一种可执行的简记记法帮助熟悉简单数学表示法的用户思考、编程或教学。也就是说,Mazy 试图用程序的观点去解释范围不超过基本函数、数列的数学记法。当然,作为基于λ演算的编程语言,Mazy 还有足够的表现力去解决更复杂的问题。
例如,Mazy 中的列表可以作数列解释。对于给定的列表 l,其第 n 项在 Mazy 中可直接表示为 l n。同时,使用递推的观点可以写出求列表长度的函数。下面的函数就是一个合法的 Mazy 程序:
 len ls =
   0, ls = ()
   len(tail ls) + 1.
Mazy 可以把列表看作是第一项 x 与剩余项 xs 的组合 x:xs,所谓 tail 函数不过是:
 tail (x:xs) = xs
那么 len 函数就可以直接写成:
 len () = 0
 len (x:xs) = len xs + 1
以上是 Mazy 在语法方面试图实现其设计意图的一个例子,用到了单一控制结构和函数单参数的语法特性。其它语法,包括块结构、列表领悟、λ形式等,以及为保证这些语法的等效性而设计的 Mazy 编程语言语义,例如完全无副作用,单一数据类型,单一数据结构,完全基于模式匹配等,都会在本文档中给出详细描述;附加的实现指导,例如尾递归优化,列表、元组共存模型,缓存机制等,则用于指导 Mazy 的实现人员更好地保证基于这些语义的程序高效地执行,以防止用户被迫转用其它语义思考和编程。

September 12, 2007

Mazy(i) plain-BNF 修订稿

这次把 Bug 基本上都修掉了,语法完备化的同时去掉了一些容易引起混淆的运算符。毕竟 Mazy means Math & Lazy, not Mess or Busy.

comment :: ;;.*(?=\n)
Number :: \d+(\.\d+)?(_[+-]?\d+)?
Identifier :: [\$]\w*[\?']?
program ::
  sentence
  sentence \n program
sentence ::
  functionDefinition
  patternMatching
functionDefinition ::
  Identifier pattern = Experssion
  Identifier pattern = \n block .
block ::
  Experssion
  program \n Experssion
  Experssion ; whereBlock
  program \n Experssion ; whereBlock
  clause
  program \n clause
  clause ; whereBlock
  program \n clause ; whereBlock
clause ::
  subClause
  subClause \n Expression
subClause ::
  Expression , Expression
  Expression , Expression \n subClause
whereBlock ::
  patternMatching
  patternMatching \n whereBlock
patternMatching :: pattern = Experssion
Expression ::
  Number
  Identifier
  Lambda
  Compound
  Operation
  Comprehension
  List
  ( Expression )
Lambda :: [ pattern -> block ]
Compound :: [ block ]
Operation ::
  Expression Expression
  prefix Expression
  Expression midfix Expression
prefix :: oneof
  + - ~ #
midfix :: oneof
  midSymbol
  % = ~= < > >= <= & | . .. : ++ <-
midSymbol :: oneof
  + - * / ^ ** //
Comprehension :: ( Expression => sequence )
List ::
  ( )
  ( Expression , )
  ( sequence )
sequence ::
  Expression , Expression
  Expression , sequence
pattern ::
  _
  Number
  Identifier
  patternOperation
  patternList
  ( pattern )
patternOperation ::
  pattern : patternList
  pattern midSymbol Number
  Number midSymbol pattern
patternList ::
  ( )
  ( pattern , )
  ( patternSequence )
patternSequence ::
  pattern , pattern
  pattern , patternSequence

最重要的变更还是新增了 block 抽象语法,没想到的时候是很痛苦的。
为语法分析增加的额外产生式也有了,这样语法和语义基本上就定下来了,下面开始写词法分析器——相当好写,输出的数据结构上用点技巧(平衡符号的开始符号指出平衡点),连带 AST 生成器一并好写。
Ray 语录:做语言要做到什么地步才叫绝?想再加1条产生式时,烦于必须再加一系列产生式;想再减1条产生式时,苦于必须再减一系列产生式——这就是所谓的“精益求精”。

September 8, 2007

Mazy(i) BNF 第一稿

我设计的 Mazy 语言第三版,意淫了一下,把 BNF 给磨出来了。28条 plain-BNF,即使算上语法分析的额外要求也不算多。

Digits :: [0-9]+
Identifier :: [a-zA-Z]+_*'*
Comment :: ".*?"
Program ::
 Sentence
 Sentence \n Program
Sentence ::
 FunctionDefinition
 PatternMatching
FunctionDefinition ::
 Identifier Pattern = Experssion
 Identifier Pattern = \n Clause .
 Identifier Pattern = \n Clause ; WhereBlock .
 Identifier Pattern = \n Program Clause ; WhereBlock .
WhereBlock ::
 PatternMatching
 PatternMatching \n WhereBlock
PatternMatching ::
 Pattern = Experssion
Expression ::
 Number
 List
 Identifier
 Operation
 Condition
 Lambda
 ( Expression )
Number ::
 Digits
 Digits.Digits
 Digits_Digits
 Digits.Digits_Digits
List ::
 ( )
 ( Expression , )
 ( Sequence )
 Comprehension
Comprehension :: ( Expression => Sequence )
Sequence ::
 Expression
 Expression , Sequence
Operation ::
 Expression Expression
 Prefix Expression
 Expression Midfix Expression
 Expression Postfix
Condition ::
 [Clause]
 [Expression ; WhereBlock]
 [Clause ; WhereBlock]
Clause ::
 SubClause
 SubClause \n Expression
SubClause ::
 Expression , Expression
 Expression , Expression \n SubClause
Lambda ::
 \ Pattern -> Expression .
 \ Pattern -> Expression ; WhereBlock .
Prefix :: oneof
 + - ~ #
Midfix :: oneof
 + - * / % ^ ** // : ++ -- = ~= < > >= <= & | ! !! $ .. <-
Postfix :: oneof
 ++ -- !

Pattern ::
 PatternAtom
 PatternList
 Pattern : PatternList
 PatternPreOpt Pattern
 Pattern PatternMidOpt PatternAtom
 PatternAtom PatternMidOpt Pattern
 Pattern PatternPostOpt
PatternList ::
 ( )
 ( PatternAtom , )
 ( PatternSequence )
PatternSequence ::
 PatternAtom
 PatternAtom \n PatternSequence
PatternAtom :: oneof
 Identifier Number _
PatternPreOpt :: oneof
 - ~
PatternMidOpt :: oneof
 + - * / ^ ** // :
PatternPostOpt :: oneof
 ++ --

新语言想设计成为基于模式匹配的语言,于是进行了模式匹配完备化的努力,最后搞出了一个带有推导功能的东西,这样函数就可以只接受一个参数,同时满足了参数之间“关系”表达的要求——终于找到可以超过 Haskell 的地方了 :)
加入了 Lambda 表达式,且带有作用域的语法都有了一个 WhereBlock,但功能其实相当于 Scheme 中的 let* 而不是 Haskell 中的 where;
列表相当于 tuple 和 list 二者的结合,列表领悟也有了;
其它一些纯属使语言更“漂亮”的小改动不值一提,感兴趣的自己看吧。

September 6, 2007

我看 D 语言

初,语言只能用有限的特性发挥有限的力量,那时有个语言自命简单(Basic),其实是弱;真正强大的语言,能用有限的特性发挥出无限的力量,但它们的老大说话总是听不清(Lisp),所以总是有人想取代它的地位;当然了,要使用无限的特性才能发挥出无限的力量的语言也是有的,只是太重了,比珍珠岩(Perlite)还重。然而还有这样一种语言,它们被设计拥有无限的特性,却只能发挥出十分有限的力量,这样的语言在被每个时代抛弃之后仍然不思悔改,现在它们那个以罩杯号码(D)命名的新任老大就是一个典型的例子。