2017-02-26 62 views
0

接受函數getLine。從IO a - > a寫一個函數?

它有一個類型

getLine :: IO String 

我如何提取此IO值的字符串。更一般地說,我該如何轉換它。

IO a 

這樣:

a 

如果這是不可能的。爲什麼我不能這樣做?

+7

因爲如果你可以做到這一點,首先要有一個IO類型沒有意義。 – sepp2k

+0

可能會重複:[*我如何解析在Haskell中的IO字符串?](http://stackoverflow.com/q/11229854/2751851)(我沒有自己關閉它,因爲它可能會使更好重複的目標)。 – duplode

回答

9

在Haskell中,當您想使用IO中的「被困」值時,您不會從IO中獲取值。相反,您也可以將要執行的操作也放入IO

例如,假設您想要使用Prelude中的length函數來檢查getLine :: IO String將產生多少個字符。

存在着所謂的fmap一個輔助功能,當專用於IO,有類型:

fmap :: (a -> b) -> IO a -> IO b 

它需要的是在不陷入IO「純粹」價值工程的功能,併爲您提供了一個功能它與陷於IO的值一起工作。這意味着代碼

fmap length getLine :: IO Int 

表示IO的行動,讀取控制檯線,然後給你它的長度。

<$>fmap的infix的同義詞,它可以使事情變得更簡單。這相當於上面的代碼:

length <$> getLine 

現在,有時操作要與IO -trapped價值本身返回IO -trapped值執行。簡單的例子:你要使用putStrLn :: String -> IO()來回寫你剛剛閱讀的字符串。在這種情況下,fmap是不夠的。您需要使用(>>=)運營商,該運營商專用於IO時,其類型爲IO a -> (a -> IO b) -> IO b。在出情況:

getLine >>= putStrLn :: IO() 

使用(>>=)到鏈IO行動有必要的,連續的味道。有一種叫"do-notation"語法糖,這有助於寫在一個更自然的方式這樣的順序操作:

do line <- getLine 
    putStrLn line 

請注意,這裏的<-不是運營商,而是由DO提供的語法糖的一部分符號。

+0

這個常見問題的簡單解釋! 「相反,你把你想要執行的操作也放到IO中!」 – luqui

1

如果你知道C然後考慮問題「我怎麼能從gets得到字符串?」一個IO String不是一些很難得到的字符串,它是一個可以返回字符串的過程 - 就像從網絡或標準輸入讀取一樣。你要運行程序獲取一個字符串。

在一個順序運行IO操作的常用方式是do符號:

main = do 
    someString <- getLine 
    -- someString :: String 
    print someString 

在上面運行getLine操作獲得String值,那麼無論你希望使用的值。

2

如果您處於do塊中,則不會涉及任何細節,您可以(非正式/不正確)將<-視爲從IO中獲取值。

例如,下面的函數需要從getLine線,並將它傳遞給純函數,只需要一個String

main = do 
    line <- getLine 
    putStrLn (wrap line) 

wrap :: String -> String 
wrap line = "'" ++ line ++ "'" 

如果編譯以此爲wrap,並在命令行運行

echo "Hello" | wrap 

你應該看到

'Hello' 
0

所以「一般」,目前還不清楚爲什麼你認爲你需要這種類型的功能,在這種情況下,它使所有的差異。

爲了完整性,應該注意的是它是可能的。確實存在名爲unsafePerformIO的基礎庫中的類型爲IO a -> a的函數。

unsafe部分是有原因的。有很少的情況下,其使用被認爲是合理的。這是一個逃生艙,要謹慎使用 - 大多數時候你會讓怪物進來而不是讓自己出去。

爲什麼你不能從IO aa?那麼至少它可以讓你通過看似純粹的功能來打破規則 - 哎!如果這是一種常見做法,那麼類型簽名和編譯器爲驗證它們所做的所有工作根本沒有意義。所有的正確性保證將會離開窗口。

哈斯克爾部分地是有趣的,正是因爲這是(通常)不可能的。

對於如何處理您的問題getLine特別看到其他答案。

相關問題