The Haskell 98 Report
top | back | next | contents | function index


2  字句構造

この章では、Haskell の下位レベルの字句構造について説明する。 このレポートを初めて読む場合には、詳細のほとんどは飛ばしてかまわない。

2.1  表記規約

次にあげる表記規約は構文を表現するために用いる。

[pattern] オプション
{pattern} 0回またはそれ以上の繰り返し
(pattern) グループ化
pat1 | pat2 選択
pat<pat'> 差---pat で生成された要素から
pat' で生成されたものを除いたもの
fibonacci 終端構文はタイプライタフォント

この節では構文は字句構文を記述するものなので、すべての白空白 は明示的に表現する。隣接するシンボルの間には暗黙の空白は存在しない。次 のような生成ルールをもつ BNF 風の構文を用いる。

nonterm -> alt1 | alt2 | ... | altn

|[...] のような超論理的構文と(タイプライタフォ ントで与えられた) |[...] のような具象終端構文を 区別しなければならない。しかし、これらの区別はふつうは文脈から明かなも のである。

Haskell では Unicode [11] 文字集 合を使う。しかしながら、ソースプログラムは現時点では Haskell の過去の バージョンで使用されていた ASCII 文字集合であるものに偏っている。

この構文は Unicode コンソーシアムが定義した Unicode 文字の性質に依存す る。Haskell コンパイラは利用可能な Unicode の新しいバージョンを使うこ とが推奨されている。

2.2  字句上のプログラム構造

program -> {lexeme | whitespace }
lexeme -> qvarid | qconid | qvarsym | qconsym
| literal | special | reservedop | reservedid
literal -> integer | float | char | string
special -> ( | ) | , | ; | [ | ] | `| { | }
whitespace -> whitestuff {whitestuff}
whitestuff -> whitechar | comment | ncomment
whitechar -> newline | vertab | space | tab | uniWhite
newline -> return linefeed | return | linefeed | formfeed
return -> a carriage return
linefeed -> a line feed
vertab -> a vertical tab
formfeed -> a form feed
space -> a space
tab -> a horizontal tab
uniWhite -> any Unicode character defined as whitespace
comment -> dashes [ any<symbol> {any}] newline
dashes -> -- {-}
opencom -> {-
closecom -> -}
ncomment -> opencom ANYseq {ncomment ANYseq}closecom
ANYseq -> {ANY}<{ANY}( opencom | closecom ) {ANY}>
ANY -> graphic | whitechar
any -> graphic | space | tab
graphic -> small | large | symbol | digit | special | : | " | '
small -> ascSmall | uniSmall | _
ascSmall -> a | b | ... | z
uniSmall -> any Unicode lowercase letter
large -> ascLarge | uniLarge
ascLarge -> A | B | ... | Z
uniLarge -> any uppercase or titlecase Unicode letter
symbol -> ascSymbol | uniSymbol<special | _ | : | " | '>
ascSymbol -> ! | # | $ | % | & | * | + | . | / | < | = | > | ? | @
| \ | ^ | | | - | ~
uniSymbol -> any Unicode symbol or punctuation
digit -> ascDigit | uniDigit
ascDigit -> 0 | 1 | ... | 9
uniDigit -> any Unicode decimal digit
octit -> 0 | 1 | ... | 7
hexit -> digit | A | ... | F | a | ... | f

字句解析は「極大大喰らい」ルールを利用しなければならない。それぞれ のポイントで単語生成ルールを見たす最長の単語を読み込む。 それで、case は予約語であるが、cases は予約語ではな い。同様に = は予約語であるが、==~= は予約語ではない。

あらゆる種類の白空白は単語の正しい区切である。

ANY の範疇にはいらない文字は Haskell プログラムでは使用で きない。使用した場合には字句エラーにならねばならない。

2.3  コメント

コメントは正当な白空白である。

通常のコメントは二つ以上の連続したダッシュ(つまり --) で構 成される字句で始まり、次の改行までである。一連のダッシュは正しい 字句の一部を構成してはならない。たとえば、"-->" や "|--" ではコメントを開始しない。これらの字句が、ダッ シュのみで構成されているわけではないからである。しかしながら、 "--foo"はコメントを開始する。

ネストしたコメントは字句 "{-" ではじまり、"-}" で 終る。正しい字句で、"{-" ではじまるものはない。それゆえ、 たとえば、"{---" は余分なダッシュは付いているが、ネストした コメントを開始する。

コメントそれ自身は字句解析されることはない。最初の未対応 "-}"によって、そのネストしたコメントは終了する。ネストしたコ メントは何段にもネストすることが可能で、"{-" があらわれるた びに新しいネストしたコメントが開始され、それは "-}" で終了す る。ネストしたコメント内の各 "{-" はそれぞれに対する "-}" で対応させる。

通常のコメント内では文字のならび "{-" や "-}" は 特別な意味を持たない。ネストしたコメント内では連続したダッシュは特別 な意味を持たない。

ネストしたコメントはコンパイラに対するプラグマとしても用いられる。 これに関しては 11 で説明する。

コードがネストしたコメントによってコメントアウトされている場合、文 字列内あるいは行末までのコメント内に {--} があ ると当該のネストしたコメントに影響する。

2.4  識別子と演算子

varid -> (small {small | large | digit | ' })<reservedid>
conid -> large {small | large | digit | ' }
reservedid -> case | class | data | default | deriving | do | else
| if | import | in | infix | infixl | infixr | instance
| let | module | newtype | of | then | type | where | _

識別子は英字ではじまり、英字、数字、アンダースコア、シングルクウォー トが0 個ないしそれ以上つづく。識別子は字句としては 2 つの名前空間 (1.4 節)に分けられ区別される。 小文字で始まる識別子(変数識別子)と、大文字で始まる識別子(構成子識 別子)である。識別子では、大文字、小文字は区別される。namenaMeName はそれぞれ別の識別子である。(最初の 2 つ は変数識別子、最後のものは構成子識別子である。)

アンダースコア "_" は小文字と看倣され、小文字が出現可能な 場所で使用することができる。しかし、"_" はそれ自身は予約済の 識別子であり、パターンにおいてワイルドカードとして使用する。未使用の 識別子に対して警告を出すコンパイラではアンダースコアで始まる識別子に 対する警告は抑制することを推奨されている。それ故、プログラマは不使用 を期待されているような "_foo" をパラメータとして使用すること も可能である。

varsym -> ( symbol {symbol | :})<reservedop | dashes>
consym -> (: {symbol | :})<reservedop>
reservedop -> .. | : | :: | = | \ | | | <- | -> | @ | ~ | =>

演算子シンボルは 1 つ以上のシンボル文字で、上記のように構成される。 また、字句としては 2 つの名前空間 (1.4 節)に分けられ区別される。

コロンそのもの ":" は Haskell のリスト構成子専用に予約され ていることに注意すること。この扱いは、他のリスト構文、"[]" や "[a,b]" などと同じ扱いになっている。

前置の符号反転の特別な構文を以外はすべての演算子は中置である。 しかし、おのおのは、セクションで部分適用演算子 (3.5節を参照)として使用することができる。 標準の中置演算子はすべてあらかじめ定義済のシンボルである。これは再定 義可能である。

このレポートの残りの部分では 6 種類の名前を使いわける。

varid (variables)
conid (constructors)
tyvar -> varid (type variables)
tycon -> conid (type constructors)
tycls -> conid (type classes)
modid -> conid (modules)

変数と型変数は小文字で始まる識別子で表現され、それ以外の 4 つは大文 字で始まる識別子で表現される。また、変数と構成子は中置形式をもち、そ れ以外の 4 つは中置形式はもたない。名前空間については 1.4 節で議論している。

外部名は場合によりモジュール名の修飾子が付けられる。 この修飾子の付加は、変数、構成子、型構成子、型クラスに対して行われ、 型変数、モジュール名に対しては行われない。被修飾名についての詳細な議 論は 5 章で行う。

qvarid -> [modid .] varid
qconid -> [modid .] conid
qtycon -> [modid .] tycon
qtycls -> [modid .] tycls
qvarsym -> [modid .] varsym
qconsym -> [modid .] consym

ひとつ被修飾名は、ひとつの字句であるので、修飾子と名前の間に空白を 置くことは許されない。字句解析の例を以下に示す。

これは このように字句解析される
f.g f . g (three tokens)
F.g F.g (qualified `g')
f.. f .. (two tokens)
F.. F.. (qualified `.')
F. F . (two tokens)

修飾子が名前の構文的扱いを変更することはない。たとえば、 Prelude.+ は中置オペレータでありプレリュード中で定義された + と同じ結合性(4.4.2節)をもつ。

2.5  数値リテラル

decimal -> digit{digit}
octal -> octit{octit}
hexadecimal -> hexit{hexit}
integer -> decimal
| 0o octal | 0O octal
| 0x hexadecimal | 0X hexadecimal
float -> decimal . decimal [exponent]
| decimal exponent
exponent -> (e | E) [+ | -] decimal

数値リテラルには 2 種類ある。整数と浮動小数である。整数リテラルは 10 進(デフォルト)、8 進( 0o あるいは 0O を接頭辞とする)あるいは 16 進 ( 0x あるいは 0X を接頭辞とする)で表現される。浮動小数は常に 10 進で 表現される。浮動小数リテラルは小数点の前と後の両方に数字を含んでいな ければならない。このようになっているのは小数点と別のドット文字の使用 法とが混同されないためである。負の数値リテラルについては 3.4 節で議論する。数値リテラルの型 付けについては、6.4.1 節で 議論する。

2.6  文字リテラルと文字列リテラル

char -> ' (graphic<' | \> | space | escape<\&>) '
string -> " {graphic<" | \> | space | escape | gap}"
escape -> \ ( charesc | ascii | decimal | o octal | x hexadecimal )
charesc -> a | b | f | n | r | t | v | \ | " | ' | &
ascii -> ^cntrl | NUL | SOH | STX | ETX | EOT | ENQ | ACK
| BEL | BS | HT | LF | VT | FF | CR | SO | SI | DLE
| DC1 | DC2 | DC3 | DC4 | NAK | SYN | ETB | CAN
| EM | SUB | ESC | FS | GS | RS | US | SP | DEL
cntrl -> ascLarge | @ | [ | \ | ] | ^ | _
gap -> \ whitechar {whitechar}\

文字リテラルは単一引用符の間に書く。たとえば、'a'。文字列 リテラルは二重引用符の間に書く。たとえば、"Hello"

エスケープコードは文字あるいは文字列の中で用いられ特別な文字を表現 する。単一引用符 ' は文字として文字列の中で使われることがあ る。しかし、文字で表現する場合は必ず、エスケープされていなければなら ないことに注意すること。同様に二重引用符を文字のとして使うことがある が、文字列の中にあるときは必ずエスケープされていなければならない。 \ は常にエスケープされなければならない。 charesc カテゴリーには以下のような移植性の ある特別な文字表現が含まれる。「アラート(\a)、「バックスペー ス」(\b)、「フォームフィード」(\f)、「改行」 (\n)、「キャリッジリターン」(\r)、「水平タブ」 (\t)、「垂直タブ」(\v)。

ユニコード文字集合用の \^X のような制御文字を含む、エスケー プ文字が供給されている。\137 のような数値エスケープは十進で 文字を指定するのに使用する。また、8 進( \o137 )や 16 進( \x37)の表現も許されている。

「極大大喰らい」ルールにより、文字列中の数値エスケープ文字は連続し た数字で構成され、その長さは任意である。同様にして曖昧なアスキーエス ケープコード "\SOH" は長さ 1 の文字列と看倣される。エスケー プ文字\& は「ナル文字」として供給され、 "\137\&9" あるいは "\SO\&H" というような文 字列を構成することができる(これらの文字列長はともに 2)。それゆえ "\&"""と同等であり、'\&' は許 されていない。文字の同等性について詳しくは 6.1.2 節に定義されている。

文字列は「隙間」-- ふたつのバックスラッシュにはさまれた空白文字 -- を含むことができ、これは無視される。これにより、行末および次の行頭に バックスラッシュを置くことで、1 行をこえる文字列を表現することができ る。たとえば、

"Here is a backslant \\ as well as \137, \
    \a numeric escape character, and \^X, a control character."

文字列リテラルは実際には文字のリストの簡略表現である。 (3.7 節参照)

2.7  レイアウト

Haskell ではレイアウトを使うことで、同じ種類の情報をもつブ レースやセミコロンを省略することが許されている。つまり、レイアウトを 考慮するコーディングスタイルも、レイアウトを無視するコーディングスタ イルも可能で、ひとつのプログラム内で自由に組合せることができる。レイ アウトは必須ではないので、Haskell のプログラムは他のプログラムから直 接に生成することができる。

レイアウトが Haskell プログラムの意味にあたえる効果は、レイアウトに より決定する位置にブレースとセミコロンを加えることにより完全に指定す ることが可能である。このように補強されたプログラムの意味は、レイアウ トを無視したものになる。

非公式ないいかたをすると、ブレースとセミコロンは以下のように挿入さ れる。レイアウト(あるいはオフサイド)規則は、キーワード whereletdoofの後で開ブレースを省略する効果 がある。このキーワードの後で、(改行があるなしにかかわらず)次の字句の 字下げが想起され、省略せされていた開ブレースが挿入される。(この字句に 先だつ空白はコメントも含まれる。) その後に続く各行については、もし、 その行が空白のみの行であるか、あるいは、字下げがより深ければ、直前の アイテムは(なにも挿入されることなく)継続される。もし、インデントが同 じ深さなら、(セミコロンが挿入され)新しいアイテムが開始される。もし、 インデントが浅ければ、(閉ブレースが挿入され)レイアウトリストは終了す る。もし、whereletdo あるいは of の直後のブレースではない字句のインデントが現在の インデントレベルと等しいかそれより小さい場合、レイアウトは開始されず、 かわりに空のリスト"{}"が挿入され、レイアウト処理は現在のイン デントレベルに対して行われる(すなわち、セミコロンあるいは閉ブレース が挿入される)。閉ブレースはレイアウトリストを含む構文範疇が終了すると ころにも必ず挿入される。不正な字句に遭遇したとき、その場所に閉ブレー スがあってもよい場合には、閉ブレースが挿入される。レイアウト規則は、 挿入されたばかりの開ブレースにのみ対応する。明示的な開ブレースは、明 示的な閉ブレースとのみ対応づけられなければならない。明示的な開ブレー スの中では、いかなるレイアウトも行われず、たとえ、行が前方の 暗黙の開ブレースより左に字下げされようとも、ブレースの外になにもつく ることはない。

B.3 節ではレイアウト規則につい てより正確な定義をしている。

これらのルールにより、ひとつの改行が複数のレイアウトリストを終了さ せうる。また、これらのルールにより以下の

f x = let a = 1; b = 2 
          g y = exp2
       in exp1

ab および g はどれも同じレイアウトリスト の一部である。

例をあげるなら、図 1 はひ とつのモジュール(いくぶん自明だが)を示しており、図 2 はそれにレイアウト規則を適用し たものを示している。特に、以下の点に注目されたい:(a) }};pop ではじまる行では、前の行の終端が、入れ子になった where 節の 深さ(3) に対応して、3度のレイアウト規則の適用を起こしていること。(b) タプルの中と case 式の中でネストしている where 節の なかに、タプルの終端が検出されたが故に閉ブレースが挿入されていること。 (c) 最後の最後にある閉ブレースはファイル終端トークンの 0 カラム字下げ によるものであること。


module AStack( Stack, push, pop, top, size ) where
data Stack a = Empty 
             | MkStack a (Stack a)

push :: a -> Stack a -> Stack a
push x s = MkStack x s

size :: Stack a -> Int
size s = length (stkToLst s)  where
           stkToLst  Empty         = []
           stkToLst (MkStack x s)  = x:xs where xs = stkToLst s

pop :: Stack a -> (a, Stack a)
pop (MkStack x s)
  = (x, case s of r -> i r where i x = x) -- (pop Empty) is an error

top :: Stack a -> a
top (MkStack x s) = x                     -- (top Empty) is an error

図 1

プログラム例


module AStack( Stack, push, pop, top, size ) where
{data Stack a = Empty 
             | MkStack a (Stack a)

;push :: a -> Stack a -> Stack a
;push x s = MkStack x s

;size :: Stack a -> Int
;size s = length (stkToLst s)  where
           {stkToLst  Empty         = []
           ;stkToLst (MkStack x s)  = x:xs where {xs = stkToLst s

}};pop :: Stack a -> (a, Stack a)
;pop (MkStack x s)
  = (x, case s of {r -> i r where {i x = x}}) -- (pop Empty) is an error

;top :: Stack a -> a
;top (MkStack x s) = x                        -- (top Empty) is an error
}

図 2

レイアウト展開したプログラム例


The Haskell 98 Report
top | back | next | contents | function index
December 2002