Hatena::Groupcadr

わだばLisperになる このページをアンテナに追加 RSSフィード

2004 | 12 |
2005 | 01 | 02 | 07 | 10 | 11 |
2006 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2007 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2008 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2009 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2010 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 08 | 09 | 10 | 11 | 12 |
2011 | 01 | 02 | 03 | 04 | 05 | 06 | 07 | 11 |

2008-01-18

俺Arc祭り 2008冬 (1)

| 10:17 | 俺Arc祭り 2008冬 (1) - わだばLisperになる を含むブックマーク はてなブックマーク - 俺Arc祭り 2008冬 (1) - わだばLisperになる

ポール・グレアム氏のArcが、この冬に公開されるらしいとのこと。

それでそのArc公開のニュースなんですが、存外、話題にもなってない様子です。

もっとドッカンドッカン騒がれるのかと思ったんですが…。

Arcの計画が世に現われたのは、2001年の11月位とのことなので、早6年。

話の流れ的には、全くつながっていないのですが、この6年前のアイディアを、そのままCommon Lispのマクロで書いて俺Arcを作ってみることにしました。

多分、結構試してみた方は結構いるんじゃないかと思うんですが、へんてこ俺Arcを作って実物のArcに思いを馳せることができるの残り僅かかも知れません。

また、俺Arcを作ってみることで、本物のArcへの理解も深まるかもしれません。

Arcはまさに今が旬なのです!

ということで、早速、ノープランでこの2001年の発表を頭から順番に作っていってみます。

下準備

とはいえなんとなく必要そうなものは予め作ってみます。


(defpackage :my-arc
  (:use :cl))

(in-package :my-arc)

(cl:defmacro macro (cl:&body body)
  (cl:if (cl:consp (second body))
	 `(cl:defmacro ,@body)
	 `(cl:defmacro ,(first body) (cl:&body ,(second body)) ,@(cddr body))))

(cl:defmacro def (cl:&body body)
  (cl:if (cl:consp (second body))
	 (cl:if (tailp body ())
		`(cl:defun ,@body)
		`(cl:defun ,(first body) ,(%add-rest (second body)) ,@(cddr body)))
	 `(cl:defun ,(first body) (cl:&rest ,(second body)) ,@(cddr body))))

(cl:defun %add-rest (expr)
  (cl:let ((l (copy-list expr)))
     (cl:let ((tail (last l)))
	     (rplacd tail `(&rest ,(cdr tail)))
	     l)))

パッケージ名は安直に、my-arc。defmacroと、defunに、macro、defという名前を付け直してみました。&restパラメータは取らないそうなので、適当にSchemeのlambdaみたいにすることにしました。割と悲しげに仕上がりました。

4. Other Principles

Arcはポリモーフィックだそうで、+で文字列の連結とかするそうです。総称関数にしようかとも思いましたが、etypecaseで分けました。これは結構やってる人は多そうです。


;; 動作
(+ "foo" "bar")
;=>"foobar"

(pr (+ #(foo) "bar"))
;#(FOO b a r)
(+ '(foo bar) '(baz))
;(foo bar baz)

(+ 0 pi)
;3.141592653589793d0

;; 定義
(def + (arg . args)
  (etypecase arg
    (string (apply #'concatenate 'string arg args))
    (vector (apply #'concatenate 'vector arg args))
    (list (apply #'concatenate 'list arg args))
    (number (apply #'cl:+ arg args))))

5. Syntax

Arcでは、文法を定義して、foo.barのような呼び出しを可能にしてみる、とのことですが、これは当然ながらしんどいので中途半端に挑戦して諦めることにしました。

fn.y => (fn y)

fn:y => (fn 'y)

[+ _ 1] => (fn (x) (+ x 1))

だそうです。

角カッコのやつは、リーダマクロでできそうです。


;; 存在しないものを捏造
(macro arcall (expr)
  (arc-to-cl expr))

;; 定義
(let ((foo -33.5))
   (arcall truncate.abs.foo))
;=> 33, 0.5

(defun arc-to-cl (expr)
  (reduce #'list
	  (map #'read-from-string (ppcre:split "\\." (string expr)))
	  :from-end 'T))

6. Arc Core

condの括弧が多いので、減らすそうです。

自分はlet、do、condの括弧は割と苦にならないタイプなんですが、そういうのは少数派なんでしょうか…。(とはいえ、もう一段階ネストした、Schemeのmatch-letはわけがわかりませんが…。)

とりあえず、安直にボディを適当に振り分けて、itを使うためにkmrclのacondに展開することにしてみました。

ちなみにここで、Lisp 1.5のcondの暗黙のprognについて語られてますが、エミュレータのLisp 1.5で試した限りでは、Lisp 1.5のcondは暗黙のprognじゃないみたいなんですよね。lambdaのボディも暗黙のprognじゃないみたいで、Lisp 1.5は謎が多いです…。

それと、nilへのcar、cdrの適用は、エラーとのこと。


;; 動作
(cond (probe-file "/tmp/") (do (pr "it -> ") (pr it) (terpri))
      nil)
;it -> /tmp/

;; 定義
(shadow 'cond)

(macro cond body
  (cl:let ((cond-body (cl:do ((b body (cl:cddr b))
			      res)
			     ((endp b) (nreverse res))
			(cl:if (cl:cdr b)
			       (push `(,(cl:car b) ,(cl:cadr b)) res)
			       (push `(t ,(cl:car b)) res)))))
	  `(kmrcl:acond ,@cond-body)))

(shadow 'car)
(shadow 'cdr)

(defmethod car ((obj null))
  (error "The value ~S is not of type LIST." obj))

(defmethod car ((obj cons))
  (cl:car obj))

(defmethod cdr ((obj null))
  (error "The value ~S is not of type LIST." obj))

(defmethod cdr ((obj cons))
  (cl:cdr obj))

7. Assignment (Scope)

ローカル変数は値を代入すると暗黙に作られて、まだ宣言されていない変数に値を代入すると、現在のブロックの残りの部分までを有効範囲とする局所変数が作られるとのこと。

ブロックは、主にdoで作成。

=は、setfに相当するとのこと。

ArcdoはCommon LispのprognでprはCommon Lisp のprincだそうです。

変数を束縛しないのは、justdoになるとのこと。ってことは、do = prognなくて、justdo = prognなんでしょうか。

とりあえず、無理矢理letに変換することにしました。


(do (= x 5)
    (cons x 'a))
;=:> (5 . A)

(do (= x 5)
  (do (= y 6)
      (list x y)))
;=> (5 6)

;; 定義
(shadow 'do)

(macro do body
  (cl:let ((vars (%x-finder '= body)))
	  `(cl:let ,vars
		   (declare (ignorable ,@vars))
		   ,@body)))

(cl:defun %x-finder (sym form &optional taglist)
  (and form
       (if (eq sym (car form))
	   (push (cadr form) taglist)
	   (dolist (c (remove-if-not #'consp form) 
		    (delete-duplicates taglist))
	     (cl:let ((tem (%x-finder sym c taglist)))
		     (cl:when tem
		       (setq taglist tem)))))))

(shadow '=)

(macro = args
  `(cl:setf ,@args))

(macro justdo body
  `(progn ,@body))

(shadow 'princ)

(setf (symbol-function 'pr) #'cl:princ)

大した内容でもないのに長くなってしまいました。まだまだあるので、続きは別エントリにします…。

ゲスト



トラックバック - http://cadr.g.hatena.ne.jp/g000001/20080118