4

HaskellのIOモナドのより深い働きを彼らの ウィキで読んでいて、このコードに出くわしました

main = do a <- ask "What is your name?"
      b <- ask "How old are you?"
      return ()
ask s = do putStr s
       readLn

それは私には理にかなっています。ask関数は、与えられた文字列を出力し、aまたはbに渡すことができる行を返す必要があります。

しかし、これをGHCiにロードすると、問題が発生します。askの使用によるReadのインスタンスがなく、GHC.Readをインポートできることを通知します。それは必要ないはずです。このコードはHaskell.orgにあったので、うまくいくはずだと思います。言語の何かが変更されましたか、それとも私が見逃している大きな理解がありますか?

4

2 に答える 2

7

ask関数だけで(問題のあるメインなしで)ファイルを作成し、それをghciにロードすると、askのタイプが次のようになっていることがわかります。

ask :: (Read a) => String -> IO a

これは、戻り型が多形であることを意味します。

問題はあなたがするとき

a <- ask "What is your name"

aコンパイラは、入力から読み取った行に対して正しい逆シリアル化関数を使用できるように、タイプが何であるかを知る必要があります。ただし、a他の場所では使用されておらず、型シグネチャも存在しないため、型推論ではの型を推測できませんa。コンパイラはあきらめて、その「あいまいなタイプ」メッセージを表示します。

これを修正するには、主に2つの方法があります。

  1. ask関数が常に同じ型を返すようにします。これは、特定の署名を追加することによって行うことができます

    ask :: String -> IO String
    

    または、をのreadLnようなものに変更しgetLineます。

  2. ポリモーフィック関数を使用している場所に型署名を追加します。型署名をask呼び出し自体に追加することができます。

    a <- ask "What is your name" :: IO String
    

    または、変数に直接追加できます

    (a :: String) <- ask "What is your name"
    

    ただし、この2番目のオプションは、デフォルトのHaskell構文では許可されていません。ファイルの最初の行として次のコメントを追加して、ScopedTypeVariables拡張を有効にする必要があります

    {-# LANGUAGE ScopedTypeVariables #-}
    
于 2013-03-09T03:28:24.153 に答える
4

コードに2つの変更を加えました。

  1. インデントを修正します-haskellは「スペースセンシティブ」であることに注意してください。コードが適切に配置されていることを確認してください
  2. 明示的な型アノテーション。これは少し注意が必要です。しかし、経験則として、動作すると予想されるコードが動作しない場合。以下に示すように、コードに型で注釈を付けてみてください。なぜこれが間に合うのか理解できるでしょう。

変更されたコードは次のとおりです。

main = do
  a <- askString "What is your name?"
  b <- askOther "How old are you?"

  putStrLn ""
  putStrLn "Name and age"
  putStrLn (a :: String)
  print (b :: Int)
  return ()

askString s = do
  putStrLn s
  getLine

askOther s = do
  putStrLn s
  readLn

編集:申し訳ありませんが、コードは実際にコンパイルされます。繰り返しますが、haskellが成熟するにつれて、askStringとaskOtherが異なるように見える理由がわかります。実行例を次に示します。

$ runghc Hello.hs
What is your name?
Arash
How old are you?
22

Name and age
Arash
22
于 2013-03-09T03:21:49.590 に答える