2011-07-08 34 views
7

Tôi muốn viết các hàm và đặt kết quả vào chuỗi.Haskell IO (Chuỗi) và Chuỗi

Tôi muốn chức năng:

read' :: FilePath -> String 

tôi sử dụng:

:t readFile 
readFile :: FilePath -> IO String 

tôi thực hiện:

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

Tôi muốn hỏi str là chuỗi hay không?

Chúng ta biết rằng:

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

Thế thì tại sao tôi không thể:

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

tôi nhận được lỗi:

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 } 

Cảm ơn bạn.

+4

ReadFile trong ký hiệu có nghĩa là bạn đang ở trong đơn IO, và đơn nguyên IO không thể thoát được! – is7s

+0

@ is7s trừ khi bạn sử dụng 'unsafePerformIO'! – alternative

+10

Quy tắc đầu tiên của unsafePerformIO là bạn không nói cho ai biết về unsafePerformIO! –

Trả lời

6

Tôi nghĩ rằng không có ai đã trả lời câu này, câu hỏi rất quan trọng, tuy nhiên:

Tôi muốn hỏi str là chuỗi hay không?

Tôi sẽ cố gắng.

Loại biến số strString, có. Tuy nhiên, phạm vi của biến này rất hạn chế. Tôi nghĩ desugaring làm-ký hiệu là cần thiết cho sự hiểu biết:

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

Tôi nghĩ rằng đây nó trở nên rõ ràng hơn tại sao str là không đủ tốt. Đó là đối số của hàm bạn chuyển đến >>=. Giá trị của nó chỉ có sẵn khi ai đó gọi hàm của bạn, chỉ xảy ra khi hành động IO chứa nó đang được thực hiện.

Ngoài ra, loại read' :: IO() được xác định không quá nhiều bởi putStrLn str, mà đúng hơn là loại trả lại của toán tử >>=. Có một cái nhìn vào nó (chuyên cho IO đơn nguyên):

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

Bạn có thể thấy rằng kết quả luôn luôn là một hành động IO b, vì vậy cố gắng để thay đổi bất kỳ đối số sẽ không giúp đỡ.

Bạn có thể đọc một số hướng dẫn đơn lẻ nếu bạn muốn hiểu lý do loại đó là như thế nào. Trực giác đằng sau nó là: bạn không thể thực hiện một hành động mà không thực hiện một hành động.

Và ở phía bên thực tế của câu hỏi, sử dụng giá trị trả về bởi một số hành động, thay vì cố gắng làm use (extractValue inputAction), mà không có ý nghĩa vì extractValue là không thể, hãy thử inputAction >>= use nếu bạn use không liên quan đến I/O hoặc fmap use inputAction nếu không.

4

Bạn nên sử dụng return str trong read' nếu bạn muốn nó trở lại str thay vì (). Bạn không thể tách IO từ loại read' vì nó không phải là chức năng thuần túy. Để hiểu rõ hơn cách đầu vào/đầu ra trong các công trình của Haskell, tôi khuyên bạn nên to read a tutorial.

+3

Trong khi điều này là chính xác, lưu ý rằng không có điểm trong ràng buộc với '<-' chỉ để ngay lập tức' return' nó. Bạn chỉ có thể viết 'read '= readFile" /path/to/test.txt "'. Đây là [luật đơn thứ hai] (http://www.haskell.org/haskellwiki/Monad_Laws). – hammar

1

Như một lý do chi tiết hơn tại sao: Nó cho phép tạp chất.

Bạn hoàn toàn không thể thực hiện IO trong khi hoạt động thuần túy hoặc hoàn toàn sẽ phá vỡ tính minh bạch tham chiếu. Về mặt kỹ thuật bạn có thể sử dụng unsafePerformIO nhưng nó sẽ phá vỡ tính minh bạch tham chiếu trong trường hợp này - bạn chỉ nên sử dụng điều đó nếu bạn có thể đảm bảo rằng kết quả là luôn là cùng một.

+1

Việc đảm bảo kết quả có đủ để yêu cầu sự tinh khiết không? Tôi có nghĩa là, có thể 'unsafePerformIO (putStrLn "hello!") 'Được gọi là tinh khiết? – Rotsor

+1

@Rotsor: Đừng quên rằng nó cũng phải an toàn với luồng. Đảm bảo độ tinh khiết bằng tay rất khó! –

+0

@camccann Tôi không chắc chắn cách diễn giải nhận xét của bạn. Bạn có đang mở rộng khái niệm về độ tinh khiết mà người dùng đơn thuần đã định nghĩa hay gợi ý những khó khăn thực tế của việc đạt được nó không? Nếu không được mở rộng, nó có thể đạt được bằng cách nói, 'safePerformIO = unsafePerformIO :: IO() ->()', phải không? Đó là xấu ... Hay không? – Rotsor

10

Chỉ để giải thích thêm một chút, trong khi các câu trả lời khác hoàn toàn chính xác, tôi muốn nhấn mạnh điều gì đó: Thứ gì đó với loại IO String không chỉ là chuỗi mà hệ thống kiểu sẽ không cho phép bạn trực tiếp. Đó là tính toán rằng thực hiện I/O để nhận chuỗi cho bạn. Áp dụng readFile vào đường dẫn tệp không trả về giá trị String nữa ngoài việc đặt miếng thịt bò bên cạnh máy xay thịt, biến chúng thành bánh hamburger một cách kỳ diệu.

Khi bạn có một số mã như thế này:

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

Điều đó không có nghĩa là bạn đang "lấy chuỗi ra khỏi getStr hai lần". Nó có nghĩa là bạn đang thực hiện tính toán hai lần và có thể dễ dàng nhận được kết quả khác nhau giữa hai.

+0

Điều tuyệt vời về hamburger ma thuật là bạn có thể ăn chúng mà không phải lo lắng về tác dụng phụ! – pat

Các vấn đề liên quan