...假設a是某種型別,例如 [Int],我們想要迭代例如獲取所有元素,對它們進行操作并在操作后列印結果,就像在以下代碼中發生的操作一樣:
fa :: [Int] -> [Int]
fa [] = []
fa (n:rln) = (n 1) : fa rln
...但在一個型別的函式中
fb :: [Int] -> IO [Int]
...這樣
main :: IO ()
main =
do
fb [3, 2, 6, 8] >>= print
...將按順序列印給定的串列 ([4, 3, 7, 9]) 并再次列印結果。
差異化和背景
我想了解“IO a”型別的性質。
這個問題不是關于如何使用 folds 和 fmap 等現成的函式進行迭代。
這不是關于列印和回傳相同結果是否有意義的問題。
到目前為止的代碼
到目前為止,我已經得到了這個“野獸”:
f :: [Int] -> IO [Int]
f ln = f' ln >>= print >>= return (f' ln)
where
f' :: [Int] -> IO [Int]
f' [] = return []
f' (n:rln) = f' rln >>= (\r -> return ((n 1) : r))
...可以像這樣在 main 中應用
main :: IO ()
main = f [3, 2, 6, 8] >>= (putStrLn . (\r -> "result=" show r))
...印刷:
[4,3,7,9]
result=[4,3,7,9]
對我來說,可疑的是f ln = f' ln >>= print >>= return (f' ln)沒有通過f' ln回傳結果的行。
問題)
型別的本質是IO a什么?
尤其是:
- 說型別的函式
IO a總是必須return鍵入是否正確a? - 正如我們所看到的,我們可以
IO ()在型別函式中執行操作IO a——但是如果我們在這樣的函式中列印,我們真的必須有這么笨拙的代碼嗎? - 什么是最有效的實施?
- 我們通常應該避免在一個函式中雙重使用(IO 操作和遞回計算)嗎?
uj5u.com熱心網友回復:
對我來說,可疑的是
f ln = f' ln >>= print >>= return (f' ln)沒有通過f' ln回傳結果的行。
我同意,這顯然是錯誤的。也許你想要這個:
f ln = f' ln >>= \x -> print x >> return x
有多種其他方式可以說同樣的事情。
說型別的函式
IO a總是必須return鍵入是否正確a?
不,有幾個原因。首先:型別的值IO a不是函式!第二:不是一個IO a動作總是回傳的;例如exitWith ExitSuccess沒有。最后,也是最關鍵的:return不是結束IO動作的唯一方法,因為還有許多其他基本動作。例如,getChar是一個IO動作而不是呼叫return——它由編譯器實作為原語。*一個型別的動作IO a總是被允許通過呼叫其他一些型別的動作IO a來完成,包括編譯器原語,而不是returning 一個型別的值a。
正如我們所看到的,我們可以
IO ()在型別函式中執行操作IO a——但是如果我們在這樣的函式中列印,我們真的必須有這么笨拙的代碼嗎?
我不知道。我不知道你認為什么笨拙。如果您可以使這個問題更加客觀,我很樂意嘗試回答它。
什么是最有效的實施?
我會寫main = mapM foo [3, 2, 6, 8] >>= print。如果您不允許我mapM從標準庫中使用,我會先實作它,例如
mapM f [] = return []
mapM f (x:xs) = do
y <- f x
ys <- mapM f xs
return (y:ys)
我們通常應該避免在一個函式中雙重使用(IO 操作和遞回計算)嗎?
不,IO 和遞回計算彼此相愛;幾乎所有main我寫過的混合IO和遞回。
*好吧,我實際上不知道是否getChar呼叫return. 它可能。但如果是這樣,那是因為它呼叫了其他一些更原始的東西,它不以 a 結尾return。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/482696.html
標籤:哈斯克尔
