LoginSignup
57
48

More than 5 years have passed since last update.

Haskell初心者が直面するghciで起こしがちなエラー4選とその対処法

Last updated at Posted at 2014-07-22

対話環境は便利ですが、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で定義した式全体の型を注釈している。

57
48
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
57
48