web-dev-qa-db-ja.com

タイプのHaskell関数:IO String-> String

Haskellで一連のコードを記述して、テキストのインデックスを作成しました。 top関数は次のようになります。

index :: String -> [(String, [Integer])]
index a = [...]

次に、この関数にファイルから読み取った文字列を指定します。

index readFile "input.txt"

ReadFileのタイプがFilePath-> IO String。

予想されるタイプ 'String'を推定タイプ 'IO String'と照合できませんでした

エラーが表示されますが、次のタイプの関数が見つかりません。

IO String -> String

成功の鍵はモナドの下のどこかにあると思いますが、問題を解決する方法を見つけることができませんでした。

36
ChrisQuignon

ReadFileアクションを呼び出し、その結果をインデックス関数に渡す関数を簡単に作成できます。

readAndIndex fileName = do
    text <- readFile fileName
    return $ index text

ただし、IOモナドはそれを使用するすべてのものを汚染するため、この関数には次のタイプがあります。

readAndIndex :: FilePath -> IO [(String, [Integer])]
42
cthulahoops

そのような機能がないのには非常に正当な理由があります。

Haskellには、機能的な純度という概念があります。つまり、同じパラメーターで呼び出された関数は常に同じ結果を返します。 only IOが許可されている場所は、IOモナド内にあります。

関数があった場合*

index :: IO String -> String

次に、次のように呼び出すことで、突然IOアクションanywhere)を実行できます。

index (launchMissiles >> deleteRoot >> return "PWNd!")

関数の純粋性は、コンパイラーが関数をより自由に並べ替えてインライン化できるため、セマンティクスを変更せずに異なるコアにスパークさせることができ、プログラマーに感覚を与えるので、失いたくない非常に便利な機能です。関数のタイプから関数が実行できることと実行できないことを知ることができる場合は、セキュリティの問題です。

*実際にはがそのような関数です。それはunsafePerformIOと呼ばれ、非常に、非常に正当な理由でそれと呼ばれます。 何をしているのか100%確信がない限り、使用しないでください!

29
Tirpen

まあ、_IO String_のIOモナド部分を取り除くことはできません。つまり、関数がIO [(String, [Integer])]を返すようにする必要があります。

モナドについてもっと学ぶことをお勧めしますが、今のところ、liftM関数を使用して回避できます。

_liftM index (readFile "input.txt")
_

liftMには次のシグネチャがあります:

_liftM :: Monad m => (a -> b) -> m a -> m b
_

非モナド関数を取り、それをモナド関数に変換します。

16
fmap index $ readFile "input.txt"

または

readFile "input.txt" >>= return . index

あなたはモナドとファンクタを調べたいかもしれません

9
James