2008年10月9日木曜日

Python, Haskell の サーチ パス - いつも何度でも (o_ _)o~†

B00005L8N5

♪ 呼んでいる  同じディレクトリの中

いつも首かしげるエラーを誘発

ポカミスは数え切れないけれど

作るな  組込みと同名のモジュール

繰り返すあやまちの

その度ぼくはエラー原因の単純さを知る

二度と同じ間違いしないと思うけれど

この両手は今日も バグ量産 ♪

 

Python のサーチパス

組込モジュール と 同名のモジュール にしない

Decimal クラスを試そうと思い、decimal.py と名前を付けたファイルを作成。

from decimal import Decimal
print Decimal("100.0")

091223-001.pngこれを実行すると、

exceptions.ImportError: cannot import name Decimal

とエラーが表示される。理由は 組込のモジュール名 と 同名のファイル を作成しているため。組込みのモジュールを使うつもりが、自分自身から import しようとしている。(+_+)

 

import したときのモジュールを検索する順序

次に上記の記述を削除した後、 decimal.py で以下のような関数を定義したとする。

def hoge():
    return "hoge"

print hoge()

091223-002.pngこれは実行しても問題ない。しかし、decimal.py を作成したディレクトリと同じディレクトリで  hoge.py ファイルを作成し、

import decimal

のように Decimal クラスを import すると `hoge’ と出力されてしまう。これは decimal.py を import してしまうため。

6. モジュール によると、

ファイルを モジュール (module) と呼びます …

ファイル名はモジュール名に接尾語 ,py がついたもの …

spam という名前のモジュールが import されると、インタプリタは spam.py という名前のファイルを現在のディレクトリ内で探し、 次に環境変数PYTHONPATH に指定されているディレクトリのリストから探します。

(太字は引用者による)

091223-004.png

 

モジュールを import する順序を変更

import するときのモジュールを探すディレクトリを表示するには、

import sys
print sys.path

とする。 print 文の結果表示されるディレクトリのリストの先頭には、実行したモジュール (ファイル) を保存している親ディレクトリ名が表示される。

先頭の要素をリストの末尾にするために、 sys.path の内容を次のようにして変更。

# import する順序を変更
sys.path.append(sys.path.pop(0))

print sys.path

from decimal import Decimal
print Decimal("100.0")

実行すると、今度は標準モジュールの方が優先され Decimal クラスを使うことができた。

091223-005.png

 

ちなみにこの類のエラーについて、ドキュメントを読んだらちゃんと書いてあった ^^;

スクリプトが標準モジュールと同じ名前をもたないようにすることが重要です。さもなければ、Python が標準モジュールを import するときにスクリプトをモジュールとして import しようと試みてしまうので注意してください。

(太字は引用者による)

 

Haskell (GHC)  のサーチパス

上記と同じような理由で、Haskell でもはまった … ^^;

091223-007.png例えば、hoge.hs ファイルの中で `Data.List’ の tails 関数を試そうと思い、

import List
main = print $ tails [1,2,3]

実行したらエラーが。。(@_@;)

List.hs:1:0: file name does not match module name `Main'

エラーの内容は、ファイルの名前とモジュールの名前 Main が一致しないと。

 

何を import しているのか?

このとき、次のように書くと実行できた。

import Data.List
main = print $ tails [1,2,3]

`.’ を使った書き方は、階層化されたライブラリを使うときの記述の仕方。ふつうのHaskellプログラミング によると、

階層化ライブラリは今後標準化されます。元のモジュールも別名として残されていますが、これからは階層化ライブラリのほうを積極的に使うべきでしょう。(p.251)

Hierarchical Haskell LibrariesMigration path には次のように書かれている。

Compatibility with Haskell 98 code will be maintained using a separate package of wrappers presenting interfaces for the Haskell 98 libraries (IO, Ratio, Directory, etc.).

この「階層化ライブラリ」に対して、以前のものは 「標準 Haskell 98 ライブラリ」と呼ばれていたと。import List と記述できるのは、 import Data.List のラッパーがあるため。新旧の対応付け一覧は、 Changes to standard Haskell 98 libraries に書かれている。

 

エラーの理由

話を戻して、 上記のコードにおける import List は、階層化ライブラリの Data.List をインポートしているのだろうか?何か別のものをインポートしようとして失敗したのではないだろうか。ここで、hoge.hs を記述した同一ディレクトリに list.hs ファイルを作成していたことを思い出した。

091223-010.png

hoge.hs の import List は、同一ディレクトリにあった list.hs を読み込んだためにエラーとなったようだ。確かに list.hs にはモジュールは宣言してないので、import された場合、Main モジュールと解釈される。

 

モジュール

ところで、Haskell のモジュールは A Gentle Introduction to Haskell: Modules によると、

Haskell のモジュールは比較的保守的にできています。モジュールの名前空間は平坦です。…

モジュール名とファイル名とは正式には結びつきません。また、ふたつ以上のモジュールを一つのファイルに含むことができます。(ひとつのモジュールを複数のファイルに分ることもできます。)

(太字は引用者による)

Python のように 「ファイル = モジュール」という図式にはなっていない。ただし、

特定の実装でモジュールとファイル名が関連するような習慣を採用するのがふつうでしょう。

とのこと。

追記(2008.10.17) : 3.2.1. Modules vs. filenames によると、

the module name must match the filename. If it doesn't, GHCi won't be able to find it.

GHC では「モジュール名とファイル名が一致するようにしないといけない」という、独自の仕様があるようだ。そのため、先ほどのエラーは、GHC が名前を手がかりにモジュールを読み込もうとしたために import List が list.hs を読み込んだのだろう。

 

サーチ パス

Python と同じように、Haskell にも 5.6.3. The search path というものがある。

GHC keeps a list of directories called the search path. For each of these directories, it tries appending basename.extension to the directory, and checks whether the file exists. The value of basename is the module name with dots replaced by the directory separator ('/' or '\', depending on the system), and extension is a source extension (hs, lhs) if we are in --make mode or GHCi, or hisuf otherwise.

091224-011.png試しに hoge.hs ファイルと同じ階層に `data’ ディレクトリを作成、その中に list.hs ファイルを作り、次のように記述した。

module Data.List where
test = putStrLn "data/list.hs"

hoge.hs では、

import Data.List
main = test

hoge.hs を実行した結果は、

data/list.hs

デフォルトでは、 ライブラリの前に現在のディレクトリが優先して検索されるようだ。

 

おまけ