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-02-25

LISP引きこもり生活 (2) LispMに少しでも近付きたい年頃

| 15:28 | LISP引きこもり生活 (2) LispMに少しでも近付きたい年頃 - わだばLisperになる を含むブックマーク はてなブックマーク - LISP引きこもり生活 (2) LispMに少しでも近付きたい年頃 - わだばLisperになる

とりあえず、前回OSの操作はできるだけSLIMEから操作して、Lispのイメージから外出することは控える、と心に決めました。

今回は、やはり気分が一番大事なので、Lispマシンを操作している感を演出するため、Lispマシンのログイン周りを真似てみようかと思います。

Lispマシンはもともとはシングルユーザ前提で開発がスタートしたこともあるのかユーザ管理については比較的素朴なようです。

とりあえず最初期のMIT CADRのマニュアルを眺めながら、Lispマシンを真似して適当に作ります。

まずは、ログインですが、loginを使い、ユーザ名の設定と、初期化ファイル(ユーザディレクトリのlispm.init)の読み込みをします。

対になるのは、logoutで、これは、ユーザが設定した変数等をログアウト時に元に戻すもののようです。

元に戻す方法は、非常に素朴で、ユーザのログイン時のみ有効な変数を設定する専用の関数を使用し、その関数は、値の設定と同時に、設定をアンドゥする式をlogout-listというリストに格納します。

そして、ログアウト時にそのリストをevalすることによって元に戻すということになっています。

ログイン変数用にlogin-setq、他の汎用的なログイン時の設定変更にはlogin-evalがあり、主に初期化ファイル内で使用します。

ちなみにTAOには、セミグローバル変数という、ユーザがログインしている間だけ有効という変ったものがあったようです。

ということで、マニュアルの説明から適当に想像して作ってみました!

Lispマシンのソースを見て作ったわけではないので、定義については適当です…。そのうちちゃんと読んでみようかと…。

ただ単にログインしただけでは、寂し過ぎるので、とりあえず、Stumpwm(Common Lispのウィンドウマネージャ)とemacsclientの組み合わせを活かして未読メールがある場合は、Wanderlustを起動するようにしてみました。(Emacs側ではWanderlustをenv-mailという関数でラッピングしています)

;; 動作

(setq foo 777)
foo
;=> 777

(login 'g000001)
;=>
;11件の未読メールがあります。メーラーを起動しますか? (y or n)
;        〜〜Wanderlust起動〜〜
;Hi.

(login-setq foo 33 bar 22)

foo
;=> 33

logout-list
;=> ((MAKUNBOUND 'BAR)(SETQ FOO 777)(MAKUNBOUND 'WATCH-MAILDIR) )

;(logout)

foo
;=> 777

bar
;=> error

;; 真似
(defparameter user-id ""
  "The value of user-id is either the name of the logged in user, 
as a string, or else an empty string if there is no user logged in.
 It appears in the who-line.")

;logout-list Variable
(defparameter logout-list ()
  "The value of logout-list is a list of forms 
which are evaluated when a user logs out.")

(defun login (name &optional (load-init))
  "If anyone is logged into the machine, login logs him out.
 (See logout .) Then user-id is set from name.
 Finally login attempts to find your INIT file. 
It first looks in \"user-id ; .LISPM (INIT)\", then in \"(INIT); 
user-id .LISPM\", and finally in the default init file
 \"(INIT); * .LISPM\". When it finds one of these that exists,
 it loads it in. login returns t ."
  (setq user-id (string name))
  (unless load-init
    (load (merge-pathnames "lispm.init" (user-homedir-pathname)))))

(defun logout (&optional name)
  "First, logout evaluates the forms on logout-list.
 Then it tries to find a file to run, looking first in 
\"user-id ; .LSPM_ (INIT)\", then in \"(INIT); user-id .LSPM_\", 
and finally in the default file \"(INIT); * .LSPM_\". 
If and when it finds one it these that exists, 
it loads it in. Then it sets user-id to an empty string and
 logout-list to nil , and returns t ."
  (declare (ignore name))
  (setq user-id "")
  (eval `(progn ,@logout-list))
  (setq logout-list () ))

(defmacro setq-return-undo (var val)
  "setqを実行し、実行内容をアンドゥする式を返す。2値目は、setqの返り値"
  `(let ((undo (if (boundp ',var)
		   '(setq ,var ',(and (boundp var) (symbol-value var)))
		   '(makunbound ',var))))
     (push undo logout-list)
     (values undo (setq ,var ,val))))

(defmacro login-setq (&rest form)
  "login-setq is like setq except that it puts a setq form on
 logout-list to set the variables to their previous values."
  `(progn
     ,@(do ((l form (cddr l))
	    (res () (cons `(nth-value 1 (setq-return-undo ,(car l) ,(cadr l)))
			  res)))
	   ((endp l) (nreverse res)))))

(defmacro login-eval (&rest form)
  "login-eval is used for functions which are \"meant to be called\" 
from INIT files, such as eine:ed-redefine-keys, 
which conveniently return a form to undo what they did.
 login-eval adds the result of the form x to the logout-list."
  `(progn
     ,@(loop :for l :in form
	     :collect `(push ,l logout-list))))

lispm.initの例

;; -*- lisp -*-

(in-package :home)

(login-setq watch-maildir '("/home/g000001/Maildir/inbox/new/*.*"
			    "/home/g000001/Maildir/WebApp/new/*.*"))
(defun mail ()
  (kmrcl:command-output "~A ~A" "emacsclient -e" "'(wl)'"))

(defun 未読さん ()
  (let ((mails (reduce (lambda (res x) (+ (length (directory x)) res))
		       watch-maildir
		       :initial-value 0)))
    (cond ((zerop mails)
	   (format t "未読メールはありません。~%"))
	  ('T (when (y-or-n-p "~D件の未読メールがあります。メーラーを起動しますか?" mails)
		(mail))))))

;;
(未読さん)
(format t "Hi.~%")