Hyperspec を讀む

Hyperspec を讀む

Common Lisp でプログラムを書いてゐながら Hyperspec を讀む習慣の無い人が多いやうなので、 その人達の爲に Hyperspec の水先案内をしてみようと思ふ。

先づは Introduction から始めて、非定期的に更新していくつもりである。

なほ、野矢茂樹さんではないが、 Hyperspec を讀む といふテキストを讀んでも Hyperspec を讀んだことにはならない。 本當に讀むべきはかういふ戯れ文ではなく Hyperspec のはうである。くれぐれも注意のこと。

Table of Contents

1 Introduction

1.1 Scope, Purpose, and History

1.1.1 Scope and Purpose

ここでのポイントは、Hyperspec といふものの位置付けである。

It is a language specification aimed at an audience of implementors and knowledgeable programmers. 

Hyperspec は「言語仕樣」を記述したものであつて、

It is neither a tutorial nor an implementation guide.

チュートリアルや實裝の手引書ではない、といふこと。

Kent M. Pitman がどこかで言つてゐたが、 Language Specification(言語仕樣)とは、 言語實裝を提供する Vendor と其の User との間の契約書のやうなものである。

1.1.2 History

讀んで字の通り。 Common Lisp が定められるまでの Lisp の發展史が書かれてある。

1.2 Organization of the Document

さらっと書いてあるが案外大切な節である。 といふのも Common Lisp 仕樣の要となる事項が何であるかが、ここにおほよそ明かにされてゐる。

Introduction 直後の第二節については次のやうに書かれてある。

For information about how programs in the language are parsed by the Lisp reader, 
see Section 2 (Syntax).

第二節では Lisp reader がプログラムを讀込んだ結果、 どういふ「オブジェクト列」へ落とされるかを定めてある。 即ち Lisp program の syntax についての取り極めがされてゐる。

For information about how programs in the language are compiled and executed, 
see Section 3 (Evaluation and Compilation).

第三節には、第二節の結果 ── Lisp reader がプログラムを讀込んだ結果返されるオブジェクト列 ── に對する「意味論」が書かれてある。

For information about data types, see Section 4 (Types and Classes). 
Not all types and classes are defined in this chapter; many are defined in chapter corresponding to their topic--
for example, the numeric types are defined in Section 12 (Numbers).

第四節は Common Lisp オブジェクトにはどういふ種類のものがあるか、 即ち「型」についての議論であり、第三節で明かにされるプログラムの意味と密接に絡んでゐる。

これ等、第二、第三、第四節が Common Lisp の仕樣の核であると言つてよく、 逆に云へば第四節までは何を置いてもきつちり讀んでおけ、といふことでもある。

For information about general purpose control and data flow, 
see Section 5 (Data and Control Flow) or Section 6 (Iteration).

第五節と第六節はプログラムを組み立てて行く際に必要となる vocabulary の説明である。

1.3 Referenced Publications

讀んで字の通り。參考圖書。

1.4 Definitions

1.4.1 Notational Conventions

表記法の説明。ここで詳細に立ち入ることはやめておくが、目を通しておくことは大切。 フォントに含意させてある意味や、 nil 'nil () '() の使ひ分けなどにも觸れてゐる。

1.4.2 Error Terminology

プログラムがエラーを起こす条件について、might や should や must などの 言葉の使ひ分けと對應させて書かれてゐる。

Situations in which errors might, should, or must be signaled are described in the standard. 

1.4.3 Sections Not Formally Part Of This Standard

注意すべきは examples や notes は仕樣の理解の爲にあるもので仕樣の一部ではないこと。 example にかうあるからと言つて、それは Common Lisp の實裝がさう動かないといけないことを意味しない。

Likewise, the ``Examples'' and ``Notes'' sections in a dictionary entry
are not considered part of the standard and could be removed if necessary. 

1.4.4 Interpreting Dictionary Entries

本文書中では、函數、變數其の他個々の項目について仕樣記述がされてゐるが、 その一つ一つはさらに詳細ないくつかの決められた名前の section に分かれて記述されてゐる。 この詳細な個々の section にどういふ名前があり、それぞれどういふ内容が記述されてゐるかを、 説明してゐる。

1.5 Conformance

仕樣に準拠するとはどういふことか、の説明。

1.6 Language Extensions

Common Lisp を擴張するとはどういふことか、の説明。

1.7 Language Subsets

Common Lisp の subset があるとすればそれはどういふものか、の説明。

1.8 Deprecated Language Features

上位互換性の爲に殘されてはゐるが、新しいコードを書くときには、 できるだけ使はないことが求められる、函數、引數、變數、などについて。

1.9 Symbols in the COMMON-LISP Package

Common Lisp プログラムを書く上での基本語彙集。 ここに出てくる symbol が各々どういふものであるかは、 見た瞬間におほよその見當が附くやうでないといけない。そのためにも常々目を通しておくこと。

2 Syntax

2.1 Character Syntax

冒頭が本節の一番のポイント。

The Lisp reader takes characters from a stream, 
interprets them as a printed representation of an object, constructs that object, and returns it. 

Lisp のプログラムテキストは、オブジェクト列を印字表現したものとして解釋される。 その印字表現を讀み、本來のオブジェクト列を生成し、返すものが Lisp reader の役割である。 1

2.1.1 Readtables

Among other things, the readtable contains the association between characters and syntax types. 

readtable とは Lisp reader が character の列を讀込む際に參照するもので、 それぞれの character が、どの Syntax Types に對應するかの對應表である。

特に斷りの無い限りにおいての readtable として Standard Readtable なるものが定められてゐる。 Standard Readtable は modify してはならない (Standard Readtable を modify してしまふと何が起こるかわからない)が、 copy-readtable を使つて得た複製に對しては、 set-macro-character などいくつかの accessor 函數を使つて變更することが認められてゐる。

Lisp reader が character の列を讀むまさにその時參照する readtable を Current Readtable と呼び、 變數 *readtable* に束縛されてゐる。 *readtable* の束縛を切換へてやれば、動的に Lisp reader の振舞を制御することができる。

2.1.2 Variables that affect the Lisp Reader

The Lisp reader is influenced not only by the current readtable, but also by various dynamic variables. 

2.1.3 Standard Characters

All implementations must support a character repertoire called standard-char; 
characters that are members of that repertoire are called standard characters. 

全ての Common Lisp 實裝は standard-char と呼ばれる型に屬する character 集合 ── これを standard characters と呼ぶ ── をサポートしてゐないといけない。

なほ、repertoire とは ISO 用語で、character 集合を符号化と獨立に論じるときに使ふ。

Common Lisp 的には、character 型 の subtype を表す type specifier 、つまり「型」を表象するものである。 standard-char が repertoire であるといふことは、 type specifier として以下のやうに用ゐることができるといふことでもある。

(values (typep #\a 'standard-char) (typep #\あ 'standard-char))
→ T and NIL

2.1.4 Character Syntax Types

When the Lisp reader is invoked, 
it reads a single character from the input stream and dispatches according to the syntax type of that character. 
Every character that can appear in the input stream is of one of the syntax types shown in Figure 2-6. 

Lisp reader が讀む character 一つ一つには、syntax type といふものが對應附けられてゐる。 syntax type は Lisp reader の状態遷移を制御する要素の一つで、以下の種類がある。

  1. Constituent Characters

    數字やシンボルを表す token を形成するもの。

  2. Invalid Characters

    入力 stream 中に現れない筈のもの。

  3. Macro Characters

    Lisp reader がそれに出合ふと特別なパーズ處理をするもの。
    ちなみに、ここで云ふ macro character と defmacro や macrolet で定義されるマクロとは全く別物なので注意のこと。

  4. Multiple Escape Characters

    verticle bar | のことだと思つておけばよい。

  5. Single Escape Character

    backslash \ のことだと思つておけばよい。

  6. Whitespace Characters

    Tab, Newline, Return, Space など。

2.2 Reader Algorithm

讀んで字の如く。Lisp reader の algorithm が書かれてある。

2.3 Interpretation of Tokens

symbol や number など、token の生成規則が述べられてゐる。

誰もが一度ははまる罠、Package System Consistency Rules に注意のこと。

2.4 Standard Macro Characters

If the reader encounters a macro character, 
then its associated reader macro function is invoked and may produce an object to be returned.

Lisp reader が macro character に出合ふと、reader macro function なるものが呼出されて、 特別なパーズ處理がされる。

プログラマーは、どの character であれ macro character として定義することができるが、 仕樣に準拠した實裝にあらかじめ定義濟みである macro character のリストが本節に擧げられてゐる。

3 Evaluation and Compilation

3.1 Evaluation

本節には Common Lisp の意味論が展開されてゐる。

3.1.1 Introduction to Environments

束縛(binding)とは、名前と値の對應のことである。

束縛は、 let など、ある種の special operator を使つてつくることができる。

An environment is a set of bindings and other information used during evaluation 
(e.g., to associate meanings with names). 

Environment とは、束縛 と其の他情報(例へば、或る變數が special なのか lexical なのか constant なのかなど) を仕舞つてある場所と考へれば好い。

Bindings in an environment are partitioned into namespaces. 

environment に仕舞はれてある束縛は namespace と呼ばれるもので仕切られてゐる。 Common Lisp の namespace は七つあつて、同じ名前でも namespace を異にするものは別のものを指示する。

  • variable
  • function, macro, special operator
  • type, class
  • catch tag
  • block tag
  • go tag
  • restart

Environment は、下記の種類に分かれてゐる。

  1. The Global Environment
    The global environment is that part of an environment that contains bindings
    with both indefinite scope and indefinite extent.
    

    プログラム實行中に、いつでも(indefinite extent)、どこからでも(indefinite scope) 參照できる束縛が置かれるのが global environment である。 defun defmacro defclass defconstant などでつくられた束縛は プログラム實行中に、いつでも、どこからでも參照できる。 つまり、それらの束縛は global environment に置かれてゐる、といふことになる。

  2. Dynamic Environments

    dynamic environment には、indefinite scope かつ dynamic extent な束縛が置かれる。

    プログラムが一旦、 ある種の form (例へば、 let catch restart-case など) へ飛込めば、其の form の實行中は、どこからでも(indefinite scope)參照できるが、 form を拔出してしまふと、もう參照ができない(dynamic extent)やうな束縛がある。 ── 例へば、 special 宣言された變數や catch tag など ── さう云つた束縛が置かれるのが dynamic environment である。

  3. Lexical Environments

    lexical environment には、lexical scope かつ indefinite extent な束縛が置かれる。

    scope と extent の理解の爲に以下の例を考へる。

    (defun compose (f g)
      #'(lambda (x)
          (funcall f (funcall g x))))
    → COMPOSE
    
    (funcall (compose #'sqrt #'abs) -9.0)
    → 3.0
    

    compose 函數に與へられた引數 fg は、 compose 函數の中でしか參照できない(lexical scope)が、 その束縛は compose を拔出た後にもずつと殘つて(indefinite extent)ゐる。

    1. The Null Lexical Environment
      The null lexical environment is equivalent to the global environment. 
      

      current lexical environment のやうな表現があつた場合に、 それは global environment も指すことがある點に注意する。

  4. Environment Objects

    Environment は Common Lisp のオブジェクトの一つである。 しかし殘念なことに、Environment object の形式其の他については implementation-dependent とされてゐて、 決められてゐるのは null lexical environmentnil で表されるといふことだけである。 これは、implementation 獨立 な code walker など、 portable なプログラム解析的プログラムを書く手段が Common Lisp には與へられてゐないことを意味する。

3.1.2 The Evaluation Model

愈々、意味論の中核 evaluation model の話。

  1. Form Evaluation

    form とは 評價 されるべき 式 のことである。 symbol, cons, self-evaluating object の區別がある。

    1. Symbols as Forms

      form が symbol であれば、それは symbol-macro であるか 變數 であるかのどちらかである。

      symbol-macro であればマクロ展開をし、その結果得られた新たな form が再歸的に評價系へ渡される。

      變數ならば、その時束縛されてゐる値が返され評價結果となる。

      變數は lexical 變數か dynamic 變數か constant 變數のどれかである。

      1. Lexical Variables

        lexical 變數 とは lexical environment からその束縛を引いて來ることのできるやうな變數のこと。

        lexical 變數 は必ず値を持つ。

      2. Dynamic Variables

        (local 或は global に) special 宣言された變數を dynamic 變數と呼ぶ。

        dynamic 變數の束縛は、 let form などにより形成される dynamic environment に置かれるか、 或は defvar のやうな宣言によつて global environment に置かれる。 なほ、後者の變數を global 變數と呼ぶこともあるが、性質としては、これは飽くまでも dynamic 變數である。 global 變數 といふ名稱が便宜上使はれることはあるが、 Common Lisp の概念として global 變數 なるものがあるわけではないので注意のこと。

        dynamic 變數は値を持たない(unbound)ことがあり得る。

      3. Constant Variables

        Constant 變數 とは名前の矛盾のやうだが、さう呼ばれてゐるので仕方がない。

        named constant と呼ばれる、 t nil pi などや、 defconstant を使つて定義された變數のこと。

        defconstant で定義したものの値を defconstant で再定義することは認められてゐるが、 豫め Common Lisp に用意されてゐる named constant の値を assign したり bind しなほしたときの動作保障はされない。 ようするにやつてはいけない。

    2. Conses as Forms

      ( で始まり ) で終る form のことで、 compound form と呼ばれる。

      compound form には、special form, macro form, function form と lambda form とがある。

      compound form の先頭(car 部)が symbol であれば、その symbol は operator の名前を指し、 その時點の(current) lexical environment から束縛値が引いてこられる。 operator の束縛如何によつて、 その form が special form か macro form か function form のどれであるかが決まる。

      compound form の先頭(car 部)が symbol で無ければ、それは lambda form である。

      1. Special Forms

        compound form の operator が special operator であれば、其れは special form であり、 それぞれ 特殊 なルールの下に評價される。

        ある種の special form は lexical 又は dynamic environment を形成する。

      2. Macro Forms

        compound form の operator が macro 定義されてゐれば、其れは macro form であり、 マクロ展開の結果得られた新たな form が再歸的に評價系へ渡される。

      3. Function Forms

        compound form の operator が special operator でもなく、macro 定義もされてゐなければ、 其れは function form であり、引數が左から右へ評價された後、 operator によつて指示される函數が呼出され、それが返す値が form の評價結果となる。

      4. Lambda Forms

        lambda form とは function form の先頭(函數名)のところに lambda 式 が置かれたものを云ふ。

    3. Self-Evaluating Objects

      數字、パス名、アレイ など、自分を評價したらそれ自身が値となるもののこと。

      1. Examples of Self-Evaluating Objects
        3 →  3
        #c(2/3 5/8) →  #C(2/3 5/8)
        #p"S:[BILL]OTHELLO.TXT" →  #P"S:[BILL]OTHELLO.TXT"
        #(a b c) →  #(A B C)
        "fred smith" →  "fred smith"
        

3.1.5 Shadowing

3.1.6 Extent

3.2 Compilation

3.2.1 Compiler Terminology

用語の説明。

いくつもあつて讀むのがしんどいが、最も注意を要するのが、

Four different environments relevant to compilation are distinguished:

四つの environment に就いてであらう。

The startup environment is the environment of the Lisp image 
from which the compiler was invoked.

startup environment とは、 compiler が起動されたとき Lisp image が持つてゐる environment のことをかう呼ぶ。

The compilation environment is maintained by the compiler 
and is used to hold definitions and declarations to be used internally by the compiler. 

compilation environment とは compiler が翻譯をする際に必要な 定義(definitions)や宣言(declarations)を持ち歩く爲の environment のこと。

The evaluation environment is a run-time environment in which
macro expanders and code specified by eval-when to be evaluated are evaluated. 

evaluation environment とは、compiler が 翻譯時に必要となる評價 ── マクロ展開 と eval-when form の評價 ── をする際に用ゐられる environment のこと。

The compilation environment inherits from the evaluation environment,
and the compilation environment and evaluation environment might be identical.
The evaluation environment inherits from the startup environment, 
and the startup environment and evaluation environment might be identical.

startup environment を繼承したものが evaluation environment となり、 evaluation environment を繼承したものが compilation environment となる。

The run-time environment is the environment in which the program being
compiled will be executed.

run-time environment とは翻譯されたプログラムを實行する際の (Lisp image が持つてゐる)environment のこと。

The term run time refers to the duration of time 
that the loader is loading compiled code or compiled code is being executed. 

ちなみに run time とは load time と execution time のことを指す。

At run time, only the run-time environment is available.

run time に關係する environment は run-time environment だけである。

3.2.3 File Compilation

Common Lisp プログラム開発の典型的な流れは、 ソースファイルを編集した後 compile (compile-file) し、その結果生成されたファイルを load (load) し、 execute すると云つたものになる。

compiler の本來の目的は、實行效率の良い形に code を翻譯(translate)することであるから、 一般には、ソースファイルに書かれてある個々の form が compile 時に評價されることはないし、 その必要もない筈である。

しかし、さうとばかり言つてゐられない情況もありうる。 compiler に知つてもらひたい情報をソースファイルに書き留めておく場合がその一つである。 例へばソースファイル(の toplevel)に、

(proclaim '(special *x*))

とあつたとしよう。

compiler の仕事は、實行效率の良い形に code を翻譯(translate)することであるから、 compiler は上の function form を翻譯はしても評價は行なはない。 (評價は runtime 、つまり翻譯結果をロードする際に行なはれる。) 評價が行なはれなければ *x* が special 變數であることを知る者は誰もゐない。 從つて、compiler が次に

(defun foo (*x*) ...)

のような form に出合つたとしても、 compiler は、この *x* を(special 變數だとは知らずに)lexical 變數だと思つて翻譯してしまふことになる。

一方、ソースファイル(の toplevel)に、

(declaim (special *x*))

のように書いておけば、これは *x* が special 變數であることを compiler に教へたことになる。 といふのは declaim は macro であり、上 form は、丁度、

(eval-when (:compile-toplevel :load-toplevel :execute)
  (proclaim '(special *x*)))

と書いたのと同じ效果を持つからである。

The eval-when special form can be used to control whether a top level form is evaluated
at compile time, load time, or both. It is possible to specify any of three situations
with eval-when, denoted by the symbols :compile-toplevel, :load-toplevel, and :execute. 

ソースファイルに記述された個々の(toplevel)form に對して、 compile time に評價して欲しいのか、run time に評價して欲しいのか、 を制御する手段を與へてくれてゐるのが eval-when special operator である。

The behavior of this form can be more precisely understood in terms of a model of
how compile-file processes forms in a file to be compiled. 
There are two processing modes, called ``not-compile-time'' and ``compile-time-too''. 

eval-when を理解するには、先づ、 compile-file が翻譯處理を行なふモードに、 ``not-compile-time'' と ``compile-time-too'' の二つのモードがあることを知る必要がある。

Successive forms are read from the file by compile-file and processed in not-compile-time mode;

特に何も指定がなければ ── eval-when に出合はなければ ── compile-file は ``not-compile-time'' モードでソースファイル中に次々に現れる form を處理していく。

  1. Processing of Top Level Forms

    本節には compile-file がソースファイル中に現れる form を處理していく手順が記述されてゐるが、 ここでは、 5 即ち eval-when form の處理に絞つて解説をする。

    5. If the form is an eval-when form, it is handled according to the next figure.
    

    次の表が eval-when が ``compile-time-too'' と ``not-compile-time'' モードをどのやうに制御しながら form をどのやうに處理するかのまとめとなつてゐる。

    CT LT E Mode Action New Mode
    Yes Yes --- --- Process compile-time-too
    No Yes Yes CTT Process compile-time-too
    No Yes Yes NCT Process not-compile-time
    No Yes No --- Process not-compile-time
    Yes No --- --- Evaluate ---
    No No Yes CTT Evaluate ---
    No No Yes NCT Discard ---
    No No No --- Discard ---

    カラム CTeval-whensituation 引數に :compile-toplevel が指定されているか否かを示す。 同樣に、 LT:load-toplevel が、 E:execute が指定されているか否かを示す。 カラム Mode は、その時のモードが ``compile-time-too'' なのか、 ``not-compile-time'' なのか、 或はモードが無關係であれば --- によつて示す。 Action カラムは eval-whenform 引數に與へられた form に對して、 以下の三つのどれが作用されるかを示す。

    • Process
      form は toplevel form として處理(process)される。 process とはその form を、
      • ``compile-time-too'' モードの時は、評價した後翻譯する。
      • ``not-compile-time'' モードの時は、單に(評價せずに)翻譯する。
    • Evaluate
      form を評價する。(翻譯は行なはれない。)
    • Discard
      form は無視される。

    New Mode カラムは eval-when form の處理後の新たなモードを示す。 --- はモード變化が無い事を示す。

10 Symbols

12 Numbers

14 Conses

15 Arrays

16 Strings

20 Files

21 Streams

22 Printer

23 Reader

Footnotes:

1

大事なのは、Common Lisp の「意味」は與へられたプログラムテキスト に對してでなく、一旦プログラムテキストを Lisp reader に讀ませておいて、 それが返してきた「オブジェクト列」に對して定められてゐる、といふこと。 これは ANSI C や Scheme など、他のプログラミング言語の仕樣と根本的に違ふ點である。

非常に簡單な例を擧げておくと、Scheme における「文字列」とはあくまで、 ダブルクウォートで始まりダブルクウォートで終るものであり、それ以外の表現は認められてゐない。 R5RS には次のやうに書かれてゐる。

<string> → " <string element>* "
<string element> → <any character other than " or \>
     | \" | \\

一方 Common Lisp においては Lisp reader が「文字列」を返すやうな表現で あればプログラムテキスト上それがどう表現されてゐるかに關係なく、 プログラムの意味が決まる。以下の foo と bar は全く同じ意味を持つ函數である。

(defun foo ()
  "            ")
(defun bar ()
  #.(make-string 12 :initial-element #\space))

Author: KURODA Hisao

Created: 2017-12-29 Fri 09:27

Emacs 25.1.1 (Org mode 8.2.10)

Validate

Comments

Popular posts from this blog

Common Lisp コーディングスタイルについて

The Art of the Metaobject Protocol を讀む