更新
這個問題有一個額外的限制,就是盡量避免IO。
這個限制本來是放在我提問的最后,好像很難被注意到。
我實際上知道如何在 Haskell 中實作我的目標,就像我知道如何在其他命令式編程語言中實作一樣 - 命令式方式。
但我沒有使用任何其他命令式編程,對嗎?我正在使用 Haskell。我想要一種 Haskell 方式,一種純粹的方式。
我重新組織了我的問題,將額外的限制重新安置在一個相對顯眼的地方。那是我的壞處。非常感謝這些快速回應。
原始問題
main :: IO ()
main =
putStrLn =<< cachedOrFetched
<$> getLine
<*> getLine
cachedOrFetched :: String -> String -> String
cachedOrFetched cached fetched =
if not $ null cached
then cached
else fetched
上面的代碼執行了兩次IO。但期望的行為是當第一個 IO 的結果不為空時跳過第二個 IO。
我知道我可以通過使用do或來實作這一點when。鑒于使用太多dos 違反了我使用 Haskell 的初衷,我可能會忍受when.
或者,還有更好的方法?更純粹的方式?
這是整個事情
大約兩周前我開始學習 Haskell。我并不期待從中得到一份作業,而只是被編程語言本身所吸引。因為據我所知,它是“最純粹的”。
起初,一切似乎都和我預期的一樣好。但是后來我發現我必須IO在我的純代碼中寫s 。我花了很長時間才找到一種方法來進行不純污染控制。Applicative Functor 似乎是救命稻草。
有了它,我可以“咖喱”不純的IO到我的純函式,從而節省了大量的do,<-和明確的IO符號。但是,我遇到了這個問題 - 我無法在純函式中跳過不必要的 IO。
再次,大量的搜索和閱讀。不幸的是,直到現在還沒有令人滿意的答案。
參考
- 避免IO
- 如何擺脫IO
uj5u.com熱心網友回復:
要跳過 IO,您必須告訴cachedOrFetched,您正在執行 IO,而不是在讀取兩行后將兩個字串傳遞給它。只有這樣,它才能有條件地運行第二個getLineIO 操作。當然,cachedOrFetched不一定需要處理IO,它可以適用于任何monad:
main :: IO ()
main = putStrLn =<< cachedOrFetched getLine getLine
cachedOrFetched :: Monad m => m String -> m String -> m String
cachedOrFetched first second = do
cached <- first
if not $ null cached
then return cached
else second
我可能會寫
main :: IO ()
main = getLine >>= ifNullDo getLine >>= putStrLn
ifNullDo :: Applicative f => f String -> String -> f String
ifNullDo fetch cached =
if not $ null cached
then pure cached
else fetch
我沒有使用任何其他命令式編程,對嗎?
是的,你是。如果您正在撰寫一個main函式或使用該IO型別,那么您正在撰寫一個命令式程式,一個接一個的 IO 操作。(純)程式需要控制何時、是否以及以何種順序運行putStrLn和getLine命令。
Haskell 只是讓命令式編程變得明確,并允許很容易地從中抽象出來,例如通過交換 monad 來運行模擬而不是實際的 IO。
我想要一種 Haskell 方式,一種純粹的方式。
Haskell 的方式是將純應用邏輯從不純的邊界中分離出來;非常喜歡埠和配接器架構。當然,這只能到此為止,對于像玩具這樣的示例,cachedOrFetched很難看出邊界應該放在哪里。但是在較大的應用程式中,您通常會為業務邏輯中的“操作”提出自己的抽象,并且只處理它們而不是IO直接處理它們。
uj5u.com熱心網友回復:
這是用do符號撰寫時您的代碼的樣子:
main :: IO ()
main = do
cached <- getLine
fetched <- getLine
putStrLn $ cachedOrFetched cached fetched
您可以看到兩個 IO 操作都在呼叫cachedOrFetched發生之前就完成了。你可以main像這樣做所有的事情:
main :: IO ()
main = do
cached <- getLine
if not $ null cached
then putStrLn cached
else do
fetched <- getLine
putStrLn fetched
原因是因為cachedOrFetched 必須能夠訪問這兩個值(它們是呼叫的引數!),然后才能決定選擇哪個。
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/374160.html
