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-04-16

返り値の型を明示してコンパイラの気を引く試み

| 15:55 | 返り値の型を明示してコンパイラの気を引く試み - わだばLisperになる を含むブックマーク はてなブックマーク - 返り値の型を明示してコンパイラの気を引く試み - わだばLisperになる

DylanでもGOOでも(もちろんGOOはDylanの影響大)、定義時に返り値の型をチェックするように書くことができる。

割と良い感じだと思うので、CLでやったらどんな感じになるのかなと思って作ってみた。

(defmacro defunt (name (&rest args) => type &body body)
  (unless (eq => '=>) (error "foo!"))
  (let ((/result (gensym)))
    `(eval-when (:compile-toplevel :load-toplevel :execute)
       (setf (symbol-function ',name)
             (lambda ,args
               ((lambda (,/result)
                  (the ,type ,/result))
                (block ,name
                  (locally
                      ,@body)))))
       ',name)))

あまり考えてない定義だけど、とりあえず

(defunt fib (n) => list
  (if (< n 2)
      n
      (+ (fib (1- n))
         (fib (- n 2)))))

;>>> Asserted type LIST conflicts with derived type (VALUES NUMBER &OPTIONAL).

のように書くと、コンパイルの時点で型が競合していることを教えてくれたりはする。

多値に対応するにはもう一捻り必要…。

DylanでL-99 (P02 最後2つの要素)

| 14:42 | DylanでL-99 (P02 最後2つの要素) - わだばLisperになる を含むブックマーク はてなブックマーク - DylanでL-99 (P02 最後2つの要素) - わだばLisperになる

マニュアルを読んでも、あまり書法が理解できないので、参考にできそうなコードを探してみたところ、DylaのCL風ライブラリというものを発見。

作者は、Scott MacKay氏ですが、元Symbolicsの人で、現在ITAに所属しているらしいというLISP街道まっしぐらな方のようです。

Dynamic Languages Wizards Series, Spring 2001にもDavid Moon氏等と一緒にパネリストとして出演してたのを見たことがあります。

とりあえず、真似するのが良いかなと思って、MacKay氏のスタイルを真似。

最初にgenericを作成して、後で特定化されたmethodを追加し、Dylanでは、返り値の型を明示することができるのですが、そこはきっちり書くというスタイルのようです。

let list = #(foo:, bar:, baz:);
format-out("%=\n", last2(list));
// => #(#"bar", #"baz")

let list = #();
format-out("%=\n", last2(list));
// => #()

// Code
module: l99-02

define generic last2
    (sequence :: <sequence>) 
 => (result :: <sequence>);

define method last2
    (sequence :: <list>)
 => (result :: <list>)
  if (2 >= size(sequence))
    sequence
  else
    last2(tail(sequence))
  end if
end method last2;

比較としてCLOSでも考えてみました。

Dylanと違ってdefgenericで返り値の型は指定できないようなので、documentationを付けてるだけです(笑)

返り値の型の指定はどうやったら良いかなと思いましたが、theを付ければ良いだけなんでしょうか。どういうのが定石なんでしょう…。

もしくは、aroundや、afterメソッドで返り値の型をチェックする、なども可能だったりするんでしょうか?

返り値の型を明示するというのは、CLのようにインクリメンタルにコンパイル可能で会話的に開発できる言語でも、割と御利益が多いんじゃないかと思うのですが、どうでしょう。

自分はコンパイラが教えてくれる情報が多くなるので、なんとなく好みです。

(defgeneric last2 (sequence)
  (:documentation "last2"))

(defmethod last2 ((sequence list))
  (if (>= 2 (length sequence))
      (the list sequence)
      (last2 (cdr sequence))))

DylanでL-99 (P01 最後のペアを返す)

| 01:27 | DylanでL-99 (P01 最後のペアを返す) - わだばLisperになる を含むブックマーク はてなブックマーク - DylanでL-99 (P01 最後のペアを返す) - わだばLisperになる

自分は何度かDylanに挑戦してはいるのですが、処理系のインストールの時点で挫折したり、動いてもコンパイルの仕方をすぐ忘れたり、S式でないので文法を憶えられなかったりして何度も挫折しています。

…ということで、L-99なのですが…。

Dylanには複数の処理系があります。Gwydion Dylanと、Open Dylanがあるのですが、今回はOpen Dylanにしておきます。

どうしてかというと、Open Dylanで最近SWANK(SLIMEのバックエンド)が動いてるらしく、SLIMEで開発できるっぽいので、Open Dylanの方が面白そうだということで…。

とりあえず、

http://www.opendylan.org/downloads/opendylan/1.0beta4/

にパッケージがあるので、ダウンロードしてインストールします。

これだけで動くようになりました。

それで、Dylanの開発環境なのですが、この辺も謎なところです。

昔のDylanのスクリーンショット等をみると非常にリッチな開発環境を持っていたようなのですが、フリーでもこういう環境はあるのでしょうか。

とりあえず、追々探って行くことにして、Emacsで書いてコンパイル、という方向で行きます。

Open Dylanの場合、make-dylan-appコマンドがインストールされます。

好きなディレクトリで、例えば、l99-01というプロジェクトの場合、

$ make-dylan-app l99-01

とすると、l99-01というディレクトリが作成され、その中に色々謎なものが生成されます。

それで、ソースファイルは、l99-01.dylanなのですが、これはHello, Woldのテンプレートになっています。

とりあえず、これを編集して、makeファイルも自動生成されるので、ソースを書いたらmakeする、ということになるみたいです。

Dylanでは、

シンボル→foo: もしくは、#"foo"

リスト→#(foo:, bar:, baz)

のようです。

formatのような、format-outがあるんですが、書法は、Cのprintf的です。

headは、carで、tailはcdr

empty?は、空かどうかを判定する総称関数。

コメントは、//で、C++っぽいです。

ちなみに、今回、一番驚いたのは、はてなのシンタックスハイライトにdylanがあったことです(笑)

let list = #(foo:, bar:, baz:);
format-out("%=\n", my-last(list));
// => #(#"baz")

// Code
module: l99-01

define function my-last(list)
  if (empty?(tail(list)))
    list;
  else
    my-last(tail(list));
  end if;
end function my-last;