2011-07-08 120 views
7

我想寫的功能,並把結果字符串。Haskell的IO(字符串)和字符串

我想功能:

read' :: FilePath -> String 

我用:

:t readFile 
readFile :: FilePath -> IO String 

我做:

read' :: IO() 
read' = do 
    str <- readFile "/home/shk/workspace/src/test.txt" 
    putStrLn str 

我想問str是字符串或不是?

我們知道:

:t putStrLn 
putStrLn :: String -> IO() 

那麼我爲什麼不能:

read' :: String 
read' = do 
    str <- readFile "/home/shk/workspace/lxmpp/src/test.txt" 
    str 

我得到錯誤:

Couldn't match expected type `[t0]' with actual type `IO String' 
    In the return type of a call of `readFile' 
    In a stmt of a 'do' expression: 
     str <- readFile "/home/shk/workspace/lxmpp/src/test.txt" 
    In the expression: 
     do { str <- readFile "/home/shk/workspace/src/test.txt"; 
      str } 

謝謝。

+4

do-notation中的readFile意味着你處於IO monad中,並且IO monad不能被轉義! – is7s

+0

@ is7s,除非您使用'unsafePerformIO'! – alternative

+10

UnsafePerformIO的第一條規則是你不告訴任何人有關unsafePerformIO! –

回答

6

我想沒有人回答了這個非常重要的問題,但:

我想問str是字符串或不是?

我會盡力。

變量str的類型是String,是的。 但是,這個變量的範圍非常有限。我認爲,脫糖的DO-符號是必要的理解:

read' = readFile "/home/shk/workspace/src/test.txt" >>= (\str -> putStrLn str) 

我覺得這裏變得更加清楚,爲什麼str不夠好。這是您傳遞給>>=的函數的參數。它的值只在有人調用你的函數時纔可用,只有在包含它的IO動作正在執行時纔會發生。

另外,read' :: IO()的類型不是由putStrLn str確定的,而是由運營商的退貨類型>>=確定的。看看它(專門給IO單子):

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

你可以看到,結果始終是一個IO b動作,所以試圖改變任何的參數也無濟於事。

如果你想明白爲什麼類型是它的方式,你可以閱讀一些monad教程。它背後的直覺是:你不能執行一個動作而不執行一個動作。

關於這個問題的實用方面,使用一些動作返回的值,而不是試圖做use (extractValue inputAction),這沒有任何意義,因爲extractValue是不可能的,如果嘗試inputAction >>= useuse確實涉及I/O ,或者如果它不是fmap use inputAction

4

如果您希望它返回str而不是(),則應該在read'中使用return str。您不能從read'類型中剝離IO,因爲它不是純函數。爲了更好地掌握Haskell輸入/輸出的工作方式,我建議您使用to read a tutorial

+3

雖然這是正確的,但請注意,僅僅立即'返回'它與'< - '綁定沒有意義。你可以寫'read'= readFile「/path/to/test.txt」'。這是[第二單元法](http://www.haskell.org/haskellwiki/Monad_Laws)。 – hammar

1

至於更詳細的原因:它允許雜質。

你絕對不是一個單純的操作過程中執行IO,或將徹底打破引用透明。從技術上講,你可以使用unsafePerformIO但它會破壞引用透明在這種情況下 - 你應該只使用,如果你能保證結果總是相同

+1

結果的保證是否足以證明純度?我的意思是,可以'unsafePerformIO(putStrLn「你好!」)被稱爲純粹? – Rotsor

+1

@Rotsor:不要忘記它也必須是線程安全的。手動確保純度很困難! –

+0

@camccann我不知道如何解釋您的評論。你是否擴展了單純用戶定義純度的概念,或者提出實現它的實際困難?如果不擴展,可以通過'safePerformIO = unsafePerformIO :: IO() - >()'來實現,對吧?哪個不好...或者不是? – Rotsor

10

爲了多說一些,而其他答案是完全正確的,我想強調一些東西:IO String類型的東西不僅僅是類型系統不會讓你直接獲得的字符串。這是一個計算,執行I/O爲您獲取一個字符串。應用readFile文件路徑不返回String值的任何不是把牛排旁邊絞肉機更神奇地把它們變成一個漢堡包。

當你有像這樣的代碼:

foo = do let getStr = readFile "input.txt" 
     s1 <- getStr 
     s2 <- getStr 
     -- etc. 

這並不意味着你「取串出的getStr兩次」。這意味着你正在執行兩次計算,並且可以很容易地在兩者之間得到不同的結果。

+0

魔術漢堡包的偉大之處在於你可以吃它們而不用擔心副作用! – pat