対話環境は便利ですが、Haskellでは強い静的型付け・純粋関数型という言語の性質から、
コンパイラと対話環境とでは少々使い勝手が違います。
ここでは、Haskell初心者がghci
を使うときに陥りがちな4つの落とし穴について紹介します。
1. 式を定義しようとしたら parse error on input `='
[原因] ghciはインタプリタなので、与えられた式全体を評価しようとする
Prelude> hoge = "foo"
<interactive>:5:6: parse error on input `='
[解決方法] let
構文を利用する
Prelude> let hoge = "foo"
Prelude> hoge
"foo"
2. 式を評価しようとしたら No instance for (Show Hoge)
- ghciはIOモナドの中にいて、式を与えるたび
print (式)
を実行している - 式を表示可能にするためには、型が
Show
クラスのインスタンスである必要がある
(print
は型Show a => a -> IO ()
を持つ。)
[原因1] 自分で定義した代数的データ型がShow
クラスに属していない
Prelude> data Tree a = Branch (Tree a) (Tree a) | Leaf a
Prelude> Leaf "hoge"
<interactive>:5:1:
No instance for (Show (main::Interactive.Tree [Char]))
arising from a use of `print'
Possible fix:
add an instance declaration for
(Show (main::Interactive.Tree [Char]))
In a stmt of an interactive GHCi command: print it
[解決方法] deriving Show
をつけて自動的にShow
クラスのインスタンスになるようにする
Prelude> data Tree a = Branch (Tree a) (Tree a) | Leaf a deriving Show
Prelude> Leaf "hoge"
Leaf "hoge"
[原因2] 簡約結果の型が関数
Prelude> let addOne = (+1)
Prelude> addOne
<interactive>:15:1:
No instance for (Show (Integer -> Integer))
arising from a use of `print'
Possible fix:
add an instance declaration for (Show (Integer -> Integer))
In a stmt of an interactive GHCi command: print it
[解決方法] 諦める or 関数適用した結果を表示するようにする
3. do式を書こうとして改行したら Empty 'do' block
(または複数行に渡る式が書けない)
[原因] 一行ずつ実行される対話環境では、インデント記法ではdo式の終端を判断できない
-- parseAtom :: Parser LispVal
parseAtom = do
first <- letter <|> symbol
rest <- many (letter <|> digit <|> symbol)
let atom = first:rest
return $ case atom of
"#t" -> Bool True
"#f" -> Bool False
_ -> Atom atom
このような式をghciで定義しようとして、
一行目を入れて改行したところで、
Prelude> let parseAtom = do
<interactive>:12:17: Empty 'do' block
となる。
[解決方法1] brace記法を利用して頑張って一行に収める
braceで囲えば、;
がデリミタとして使える。
let parseAtom = do {
first <- letter <|> symbol; rest <- many (letter <|> digit <|> symbol); let {atom = first:rest}; return $ case atom of { "#t" -> Bool True; "#f" -> Bool False; _ -> Atom atom } } :: Parser LispVal -- ここまで改行なし
このように頑張って一行に収めればdo
構文をghci
で利用できる。
case
構文もbrace記法で記述できる。
ちなみに、brace記法の場合、do
構文の中でlet
構文を利用する場合、もう一段{
~ }
で囲ってやらなきゃいけない。
(http://stackoverflow.com/questions/15273158/how-do-i-use-a-let-within-a-do-block-in-ghci)
[解決方法2] ghciのmultiline
コマンドを実行する
-
:
から始まる行はghciのコマンド -
:{
と:}
のセットとなったmultiline
コマンドを利用することで、複数行に渡る式を記述できる
ghci> :{
ghci| let parseAtom = do
ghci| first <- letter <|> symbol
ghci| rest <- many (letter <|> digit <|> symbol)
ghci| let atom = first:rest
ghci| return $ case atom of
ghci| "#t" -> Bool True
ghci| "#f" -> Bool False
ghci| _ -> Atom atom
ghci| :} :: Parser LispVal
この場合、一行目のparseAtom
の先頭よりも二行目以降が右側にインデントされていないと、
インサイドルールにより構文エラーとなる。どこかからソースをコピペしようとする場合、先頭にlet
を加えただけで実行しようとすると構文エラーになるので、二行目以降の各行を4文字分インデントする必要がある。
4. 式を評価しようとしたら Ambiguous type variable in the constraint
[原因] あなたが評価しようとしている式には文脈が足らない
- HaskellのようなHM型の静的型付け言語は式に多相性があり、型推論を行うことで最終的な型が決定される
- ソースコードの一部を抜き出してインタプリタで実行するだけでは、型を推論するのに情報が足らない場合がある
[解決方法] 型注釈を与える
let hogehoge = fugafuga :: Hoge
let
構文の末尾に型注釈を書ける
multiline
コマンドを利用しlet式で長い関数を定義用とする場合は、
Prelude> :{
Prelude | let parseString = do
Prelude | char '"'
Prelude | x <- many (noneOf "\"")
Prelude | char '"'
Prelude | return $ String x :: Parser LispVal
Prelude | :}
こんな感じ。式x
のみを注釈しているように見えるが、ちゃんとletで定義した式全体の型を注釈している。