-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbugs.xml
More file actions
387 lines (311 loc) · 25.6 KB
/
bugs.xml
File metadata and controls
387 lines (311 loc) · 25.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
<?xml version="1.0" encoding="UTF-8"?>
<chapter id="bugs-and-infelicities">
<title>既知のバグと問題点</title>
<sect1 id="vs-Haskell-defn">
<title>Haskell 標準とGlasgow Haskell、言語上の不準拠点
</title>
<indexterm><primary>GHC vs the Haskell standards</primary></indexterm>
<indexterm><primary>Haskell standards language vs GHC</primary></indexterm>
<para>この節では、Haskell 98およびHaskell 2010の実装についてのGlasgow Haskellの問題点を列挙する。クラッシュ、スペースリークなどの良くない現象については「うまくいかないとき」の節(<xref linkend="wrong"/>)も参照せよ。</para>
<para>この制限一覧は、(大体)Haskellレポートの順で並んでいる。</para>
<sect2 id="haskell98-divergence">
<title>Haskell 98およびHaskell 2010からの逸脱</title>
<para>デフォルトではGHCは(大部分)Haskell 2010のコンパイラのように振る舞おうとするが、<literal>-XHaskell98</literal>や<literal>-XHaskell2010</literal>フラグを使うことで特定の言語バージョンのように振る舞うようにすることもできる。標準からの逸脱は以下に記述する。特に述べていない場合、逸脱はHaskell 98、Haskell 2010、デフォルトの各モードに関するものである。</para>
<sect3 id="infelicities-lexical">
<title>字句的構文</title>
<itemizedlist>
<listitem>
<para>GHCでは、修飾された識別子に関して、ある種の字句規則がHaskellレポートのものとわずかに異なっている。<replaceable>module</replaceable><literal>.</literal><replaceable>reservedop</replaceable>という形のもの、例えば<literal>M.\</literal>があったとき、<literal>M</literal>と<literal>.\</literal>という二つの字句要素ではなく、一つの修飾された演算子と解釈される。</para>
</listitem>
</itemizedlist>
</sect3>
<sect3 id="infelicities-syntax">
<title>文脈自由文法</title>
<itemizedlist>
<listitem>
<para>Haskell 98モードおよびデフォルトモード(Haskell 2010モードはそうでない)において、GHCは、<literal>do</literal>式でのレイアウト規則について、多少寛容である。具体的には、「入れ子の内側の文脈は、外側の文脈よりも多くインデントされなければならない」という規則を緩和して、外側の文脈が<literal>do</literal>の場合、内側の文脈が外側の文脈と同じ水準でも良いとする。</para>
<para>例えば、GHCは以下のコードを受け付ける。
<programlisting>
main = do args <- getArgs
if null args then return [] else do
ps <- mapM process args
mapM print ps</programlisting>
この振る舞いは<literal>NondecreasingIndentation</literal>拡張によって制御される。
</para>
</listitem>
<listitem>
<para>Haskell 98は(Haskell 2010は違う)パース中に式の中で結合性の解決を行なうことを要求するが、GHCはこれを行なわない。例えば、Haskell レポートによれば、以下のものは合法的なHaskellである。
<programlisting>
let x = 42 in x == 42 == True</programlisting>
そして、これは以下のようにパースされるとする。
<programlisting>
(let x = 42 in x == 42) == True</programlisting>
これは、レポートによれば、<literal>let</literal>式は<quote>できるだけ右へ拡張される</quote>からである。二番目の等号を越えて拡張しようとするとパースエラーが発生する(<literal>==</literal>は非結合的なので)ため、<literal>let</literal>式はそこで終わる。GHCは単純に式全部を飲み込む。パースは次のようになる。
<programlisting>
(let x = 42 in x == 42 == True)</programlisting></para>
</listitem>
<listitem>
<para>Haskellレポートは、キーワードで始まるある種の式の前に単項<literal>-</literal>を置くことを許すので、<literal>- case x of ...</literal>や<literal>- do { ... }</literal>といったものが書ける。GHCはこれを許さない。単項<literal>-</literal>を置くことができるのは、関数として適用し得るような式の前だけである。
</para>
</listitem>
</itemizedlist>
</sect3>
<sect3 id="infelicities-exprs-pats">
<title>式とパターン</title>
<para>デフォルトのモードでは、GHCはある種のプログラムの定義され度合いを本来よりも僅かに大きくする。例として以下を考えよ。
<programlisting>
f :: [a] -> b -> b
f [] = error "urk"
f (x:xs) = \v -> v
main = print (f [] `seq` True)
</programlisting>
これは<literal>error</literal>を呼ぶべきだが、実際には<literal>True</literal>を表示する。理由: GHCは<literal>f</literal>を以下のようにη展開する。
<programlisting>
f :: [a] -> b -> b
f [] v = error "urk"
f (x:xs) v = v
</programlisting>
大部分のプログラムについては、これは僅かだが有意に効率を改善する。これが害になるようなプログラムは少ない。この偽の「最適化」を無効にするには<option>-fpedantic-bottoms</option>を使う。</para>
</sect3>
<sect3 id="infelicities-decls">
<title>宣言と束縛</title>
<para>データ型の文脈は、次の版の言語標準では取り除かれることが決まったので、デフォルトモードにおいてGHCはこれを受け付けない。この振る舞いは<option>DatatypeContexts</option>拡張で制御できる。<xref linkend="datatype-contexts" />を見よ。</para>
</sect3>
<sect3 id="infelicities-Modules">
<title>モジュールシステムとインタフェースファイル</title>
<para><xref linkend="mutual-recursion"/>にある通り、相互再帰的なモジュールのループがある場合、それを<literal>hs-boot</literal>ファイルを使って断つことをGHCは要求する。これはバグと言うより不幸な出来事である。Haskellレポートは次のように述べている(<ulink url="http://haskell.org/onlinereport/modules.html#sect5.7">5.7節</ulink>、<ulink url="http://www.sampou.org/haskell/report-revised-j/modules.html#sect5.7">和訳</ulink>)。「使うHaskell処理系によっては、相互再帰的なモジュールを分割コンパイルするとき、インポートされるモジュールに追加の情報を持たせ、そのモジュールがコンパイルされる前に参照できるようにすることが要求されるかもしれない。相互再帰に対処するため、エクスポートされる値全てについて、明示的な型シグネチャが必要とされるかもしれない。分割コンパイルの精密な詳細はこのレポートでは定義しない。」
</para>
</sect3>
<sect3 id="infelicities-numbers">
<title>数値、基本型、組込みクラス</title>
<variablelist>
<varlistentry>
<term>Numのスーパークラス</term>
<listitem>
<para><literal>Num</literal>クラスは、スーパークラスとして<literal>Show</literal>および<literal>Eq</literal>を持たない。
</para>
<para>以下のようにすることで、Haskell98/Haskell2010とGHCの両方で動くコードを作ることができる。
<itemizedlist>
<listitem>
<para><literal>Num</literal>のインスタンスを作るときは必ず、<literal>Show</literal>と<literal>Eq</literal>のインスタンスにもする。</para>
</listitem>
<listitem>
<para><literal>Num t</literal>という制約を持つ関数またはインスタンスまたはクラスを与える場合は必ず、<literal>Show t</literal>と<literal>Eq t</literal>制約も与える。</para>
</listitem>
</itemizedlist>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>Bitsのスーパークラス</term>
<listitem>
<para><literal>Bits</literal>クラスはスーパークラスとして<literal>Num</literal>を持たない。従って<literal>bit</literal>、<literal>testBit</literal>、<literal>popCount</literal>のデフォルトメソッドを持たない。
</para>
<para>以下のようにすることで、Haskell2010とGHCの両方で動くコードを作ることができる。
<itemizedlist>
<listitem>
<para>型を<literal>Bits</literal>のインスタンスにするときは常に<literal>Num</literal>のインスタンスにもする。
</para>
</listitem>
<listitem>
<para><literal>Bits t</literal>制約を持つ関数、インスタンス、クラスを与えるときは常に、<literal>Num t</literal>制約も持たせる。
</para>
</listitem>
<listitem>
<para><literal>Bits</literal>のインスタンスでは、常に<literal>bit</literal>、<literal>testBit</literal>、<literal>popCount</literal>の各メソッドを定義する。
</para>
</listitem>
</itemizedlist>
</para>
</listitem>
</varlistentry>
<varlistentry>
<term>追加のインスタンス</term>
<listitem>
<para>
以下のインスタンスが追加で定義される。
</para>
<programlisting>
instance Functor ((->) r)
instance Monad ((->) r)
instance Functor ((,) a)
instance Functor (Either a)
instance Monad (Either e)
</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term>配列要素の複数回定義は検査されない</term>
<listitem>
<para>以下のコード断片は致命的エラーを出すべきだが、実際にはそうならない。
<programlisting>
main = print (array (1,1) [(1,2), (1,3)])</programlisting>
GHCの<literal>array</literal>の実装では、配列要素の値はリスト中に最後に現れた(index,value)の対から採られ、重複は検査されない。これは純粋単純に効率上の理由からである。
</para>
</listitem>
</varlistentry>
</variablelist>
</sect3>
<sect3 id="infelicities-Prelude">
<title><literal>Prelude</literal>サポートに関すること</title>
<variablelist>
<varlistentry>
<term>任意の大きさのタプル</term>
<listitem>
<para>タプルの大きさは現在100までに制限されている。ただし、タプルに関する標準インスタンス(<literal>Eq</literal>、<literal>Ord</literal>、<literal>Bounded</literal>、<literal>Ix</literal> <literal>Read</literal>、<literal>Show</literal>)は16要素のタプルまでについて<emphasis>しか</emphasis>使えない。</para>
<para>この制限は簡単に覆せるので、これで困ることがあったら教えてほしい。</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>splitAt</literal>の意味論</term>
<!-- 訳注: listitemタグが欠けてる -->
<para>Haskellレポートでの仕様よりも、<literal>Data.List.splitAt</literal>の正格性が高い。具体的には、レポートによると<programlisting>splitAt n xs = (take n xs, drop n xs)</programlisting>であり、従って<programlisting>splitAt undefined undefined = (undefined, undefined)</programlisting>であるが、GHCの実装は第一引数について正格であり、よって<programlisting>splitAt undefined [] = undefined</programlisting>である。</para>
</varlistentry>
<varlistentry>
<term>整数の<literal>Read</literal></term>
<listitem>
<para>整数型に関する<literal>Read</literal>クラスのGHCでの実装は、十六進と八進のリテラルにも対応している。(Haskell 98レポート中のコードは対応していない)。従って、GHCでは以下のものが動作する。</para>
<programlisting>read "0xf00" :: Int</programlisting>
<para>こうなっている理由としては、<literal>readLitChar</literal>が十六進や八進のエスケープを受け付けるのに、整数についてそうしないのは整合性に欠けるというのが考えられる。</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>isAlpha</literal></term>
<listitem>
<para>Haskell 98での<literal>isAlpha</literal>の定義は以下のようになっている。</para>
<programlisting>isAlpha c = isUpper c || isLower c</programlisting>
<para>GHCでの実装では、Unicodeのアルファベット文字のうち、小文字でも大文字でもない文字についても、<literal>isAlpha</literal>はこれをアルファベットだとみなすが、この点でHaskell 98から逸脱している。</para>
</listitem>
</varlistentry>
<varlistentry>
<term><literal>hGetContents</literal></term>
<listitem>
<para>遅延I/Oにおいてエラーが発生した場合、Haskell 98の仕様ではそれを捨てることになっている(Haskell 98レポートの21.2.2節を見よ)が、それに反して例外が投げられる。投げられるのは、原因となったIO操作がIOモナド中で実行された場合に投げられるであろう通常のIO例外であり、<literal>System.IO.Error.catch</literal>か<literal>Control.Exception.catch</literal>で捕捉できる。</para>
</listitem>
</varlistentry>
</variablelist>
</sect3>
<sect3 id="infelicities-ffi">
<title>外部関数インタフェース</title>
<variablelist>
<varlistentry>
<term><literal>hs_exit()</literal>の後に<literal>hs_init()</literal>を呼ぶことが許されない</term>
<listitem>
<para>FFI仕様の要求するところによると、実装は、<literal>hs_exit()</literal>によって停止した後に再び初期化を行うのに対応していなければならないが、GHCは現在これを行えない。</para>
</listitem>
</varlistentry>
</variablelist>
</sect3>
</sect2>
<sect2 id="haskell-98-2010-undefined">
<title>Haskell 98とHaskell 2010の未定義動作についてのGHCの解釈</title>
<para>この節では、Haskell 98で未定義、または実装固有とされている諸問題について、GHCの立場を解説する。</para>
<variablelist>
<varlistentry>
<term>
型<literal>Char</literal>
<indexterm><primary><literal>Char</literal></primary><secondary>size of</secondary></indexterm>
</term>
<listitem>
<para>ISO-10646標準に従って、GHCでの<literal>maxBound :: Char</literal>は<literal>0x10FFFF</literal>である。</para>
</listitem>
</varlistentry>
<varlistentry>
<term>
大きさ付き整数型
<indexterm><primary><literal>Int</literal></primary><secondary>size of</secondary></indexterm>
</term>
<listitem>
<para>GHCでは、<literal>Int</literal>型の精度はホストアーキテクチャでのアドレスの大きさと同じである。言い替えると、32ビットの機械では32ビットだし、64ビットの機械では64ビットである。</para>
<para><literal>Int</literal>の数値演算はオーバーフロー<indexterm><primary>overflow</primary><secondary><literal>Int</literal></secondary></indexterm>の検査を行わない。従って、<literal>Int</literal>の全ての演算は2<superscript><replaceable>n</replaceable></superscript>を法として行われる。ただし<replaceable>n</replaceable>は<literal>Int</literal>型のビット数である。</para>
<para><literal>fromInteger</literal><indexterm><primary><literal>fromInteger</literal></primary></indexterm>関数(従って<literal>fromIntegral</literal><indexterm><primary><literal>fromIntegral</literal></primary></indexterm>も)は、<literal>Int</literal>に変換する時は特別は場合である。<literal>fromIntegral x :: Int</literal>の値は、まず<literal>(abs x)</literal>の下位<replaceable>n</replaceable>ビットを採り、<literal>x</literal>の符号を掛ける(<replaceable>n</replaceable>ビットの2の補数演算で)ことで得られる。この振る舞いは、<literal>0xffffffff :: Int</literal>と書いた場合に、結果の<literal>Int</literal>にビットパターンが保存されるように選ばれた。</para>
<para>例えば<literal>-3</literal>のような負のリテラルは、Haskellレポートによれば(注意深く読めば)、<literal>Prelude.negate (Prelude.fromInteger 3)</literal>を意味する。従って<literal>-2147483648</literal>は<literal>negate (fromInteger 2147483648)</literal>である。<literal>fromInteger</literal>は表現の下位32ビットを採るので、<literal>fromInteger (2147483648::Integer)</literal>を型を<literal>Int</literal>として計算すると<literal>-2147483648::Int</literal>になる。次に<literal>negate</literal>演算はオーバーフローするが、これは検査されないので、<literal>negate (-2147483648::Int)</literal>は単に<literal>-2147483648</literal>になる。まとめると、<literal>minBound::Int</literal>をリテラルとして書いた場合、予期した通りの意味になる。(しかしこれは一般には保証されていない。)</para>
<para><literal>fromIntegral</literal>関数は、大きさ固定の整数型(<literal>Int8</literal>、<literal>Int16</literal>、<literal>Int32</literal>、<literal>Int64</literal>、および対応する符号なしの<literal>Word</literal>系の型)間で変換するときも、ビットパターンを保存する。ライブラリ説明書中の<literal>Data.Int</literal>と<literal>Data.Word</literal>モジュールを見よ。</para>
</listitem>
</varlistentry>
<varlistentry>
<term>検査なしの浮動小数演算</term>
<listitem>
<para><literal>Float</literal>と<literal>Double</literal>の数値の演算は、オーバーフロー、アンダーフロー、その他の悲しい現象について検査されない。(ただし、アーキテクチャによっては、浮動小数オーバーフローや精度の損失を捉え、浮動小数例外として報告する場合がある。この場合おそらくプログラムは終了させられる)<indexterm><primary>floating-point exceptions</primary></indexterm></para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
<sect2 id="ffi-divergence">
<title>FFI仕様からの逸脱</title>
<variablelist>
<varlistentry>
<term><literal>hs_exit()</literal>の後に<literal>hs_init()</literal>を呼ぶことが許されない</term>
<listitem>
<para>FFI仕様の要求するところによると、実装は、<literal>hs_exit()</literal>によって停止した後に再び初期化を行うのに対応していなければならないが、GHCは現在これを行えない。</para>
</listitem>
</varlistentry>
</variablelist>
</sect2>
</sect1>
<sect1 id="bugs">
<title>既知のバグと問題点</title>
<para>バグトラッカには、報告されたもののまだ修正されていないGHCのバグが列挙されている。<ulink url="http://ghc.haskell.org/trac/ghc/">GHC Trac</ulink>を見よ。加えて、GHCには、既知のバグや問題点として以下のものがある。これらはより永続的である。つまり、これらはどれも短期的に修正される可能性が低い。</para>
<sect2 id="bugs-ghc">
<title>GHCのバグ</title>
<itemizedlist>
<listitem>
<para>GHCは、漏れのあるパターンや重複パターンについて警告することができる。(<xref linkend="options-sanity"/>を見よ)。これは大抵の場合正しく動作するが、そうでないこともある。文字列パターンやガードがあると混乱し、不要な警告を出力することがある。本当は重複検査のコード全体を総点検する必要がある。</para>
</listitem>
<listitem>
<para>GHCは、データ型の文脈に、そのデータ型のパラメタ以外の型変数が出現することを許していない。例を挙げる。
<programlisting>
data C a b => T a = MkT a
</programlisting>
<literal>MkT</literal>の型は以下のようになるはずである。
<programlisting>
MkT :: forall a b. C a b => a -> T a
</programlisting>
原理的には、関数従属を使った適切なクラス宣言があれば、この型が曖昧でないこともあり得る。それでもGHCはこれを受け付けない。データ型宣言の文脈中に現れる型変数はそのデータ型の型パラメタのどれかでなければならない。</para>
</listitem>
<listitem>
<para>再帰をデータ型にエンコードする標準的な方法を使うと、GHCのインライン化器を停止しないようにすることができる。</para>
<programlisting>
data U = MkU (U -> Bool)
russel :: U -> Bool
russel u@(MkU p) = not $ p u
x :: Bool
x = russel (MkU russel)
</programlisting>
<para>この不自然な例以外に、GHCを発散させるプログラムの形は見付かっていず、この問題を修正しようとするとあらゆるコンパイルにオーバーヘッドが掛かることになるので、このバグは修正されないままになっている。さらなる背景情報は<ulink url="http://research.microsoft.com/~simonpj/Papers/inlining/">Secrets of the GHC inliner</ulink>にある。</para>
</listitem>
<listitem>
<para>32ビットのx86プラットフォームでネイティブコード生成器を使う場合、<option>-fexcess-precision</option><indexterm><primary><option>-fexcess-precision</option></primary></indexterm>オプションが常に有効になる。これにより、プログラムのコンパイルのされかた(例えば最適化設定)に依存して、ある種の浮動小数点演算が、32ビットや64ビットが意図されているにもかかわらず80ビットで為される。この結果、浮動小数点演算は非決定的になる。最適化が有効だと浮動小数点の結果が異なることがある。最悪の場合、参照透明性が守られない。<literal>let x = E1 in E2</literal>が<literal>E2[E1/x]</literal>とは異なる値に評価され得るからである。</para>
<para>回避策の一つに<option>-msse2</option><indexterm><primary><option>-msse2</option></primary></indexterm>オプション(<xref linkend="options-platform"/>)を使うというものがある。これはx87命令セットの代わりにSSE2命令セットを使ってコードを生成する。SSE2は全ての浮動小数点演算を正しい精度で行なうので、決定的な結果を与える。ただし、これはSSE2に対応したプロセッサ(Intel Pentium 4またはAMD Athlon 64以降)においてのみ動作することに注意。このため、このオプションはデフォルトで有効になっていない。GHC付属のライブラリはおそらくこのオプションなしでビルドされている(あなたが自分でビルドしたのでないかぎり)。
</para>
</listitem>
<listitem>
<para>弱参照と遅延評価の間に悪い相互作用があることが分かっている。特に、弱参照への参照を含むサンクを別の弱参照の中に置くと、実行時にクラッシュする場合があることが分かっている。詳細は<ulink url="https://ghc.haskell.org/trac/ghc/ticket/11108">Trac #11108</ulink>を見よ。</para>
</listitem>
</itemizedlist>
</sect2>
<sect2 id="bugs-ghci">
<title>GHCi(対話的GHC)のバグ</title>
<itemizedlist>
<listitem>
<para>GHCiは、利用中のスコープのモジュールにある<literal>default</literal>宣言を考慮せず、コマンド行に打ち込まれた式については、デフォルトのデフォルト化を行う。すなわち、<literal>default(Int,Double)</literal>である。</para>
<para>GHCiがそれぞれのモジュールのデフォルト設定を覚えておいて、「現在の」モジュール(どういう意味かは別にして)のものを使うようにした方が良いだろう。</para>
</listitem>
<listitem>
<para>Windowsでは、GNU ld/BFDにバグがあり、0xffff個よりも多くの再配置のある、正しくないPEオブジェクトファイルを出力することがある。このバグに影響されたパッケージをGHCiがロードしようとすると、次のような形式のメッセージが得られる。
<screen>
Loading package javavm ... linking ... WARNING: Overflown relocation field (# relocs found: 30765)
</screen>
我々が最後に確認したときはこのバグはまだBFDのコードベースにあり、2001年あたりにこのバグが報告されて以来、興味がもたれた様子もない。
</para>
<para>回避するには、パッケージを構成する.oファイルを複数個の.oに分ければ良い。これは「base」パッケージが行っていることである。</para>
</listitem>
</itemizedlist>
</sect2>
</sect1>
</chapter>
<!-- Emacs stuff:
;;; Local Variables: ***
;;; sgml-parent-document: ("users_guide.xml" "book" "chapter") ***
;;; End: ***
-->