2012-08-18 30 views
6

Tôi mới sử dụng Haskell và tôi cố gắng hiểu cách thực hiện IO chính xác.Haskell IO: Không thể khớp với loại mong đợi `IO a0 'với loại thực tế

Các công trình sau ok:

main = do 
    action <- cmdParser 
    putStrLn "Username to add to the password manager:" 
    username <- getLine 
    case action of 
    Add -> persist entry 
     where 
     entry = Entry username "somepassword" 

Trong khi kết quả như sau do lỗi biên dịch:

main = do 
    action <- cmdParser 
    case action of 
    Add -> persist entry 
     where 
     entry = Entry promptUsername "somepassword" 

promptUsername = do 
    putStrLn "Username to add to the password manager:" 
    username <- getLine 

Lỗi là ở đây:

Couldn't match expected type `IO b0' with actual type `[Char]' 
Expected type: IO b0 
    Actual type: String 
In the expression: username 
[...] 

gì đang xảy ra ở đây? Tại sao phiên bản đầu tiên hoạt động, trong khi phiên bản thứ hai thì không?

Tôi biết rằng trong Stack Overflow có một số câu hỏi tương tự như thế này, nhưng không ai trong số họ dường như giải thích vấn đề này với tôi.

Trả lời

8

usernameString, nhưng promptUsernameIO String. Bạn cần phải làm điều gì đó như:

username <- promptUsername 
let entry = Entry username "somepassword" 
persist entry 
+5

Tôi sẽ mở rộng. Đây là ký hiệu gây nhầm lẫn. Mã như 'do {a; b <- c; d b}' thực sự là viết tắt của 'a >> = \ _ -> c >> = \ b -> d b'. Những người đến từ thế giới bắt buộc nghĩ về '<-' ở đây như một nhà điều hành phân công. Không phải vậy. Mỗi dòng trong ký hiệu được dịch thành hàm ẩn danh và đối số dấu '<-' của hàm đó. Người ta nên được khuyến khích mạnh mẽ để đọc về monads và ứng dụng thực tế của họ để làm quen với họ. – permeakra

+0

Tôi thực sự có thể hiểu được vấn đề và giải pháp mà không có kiến ​​thức về monads. –

0

Đây là một biến thể khác.

main = do 
    action <- cmdParser 
    case action of 
    Add -> do username <- promptUsername 
       let entry = Entry username "somepassword" 
       persist entry 

promptUsername :: IO String 
promptUsername = do 
    putStrLn "Username to add to the password manager:" 
    getLine 

-- fake definitions to make things compile 

persist :: Entry -> IO() 
persist = print 

cmdParser :: IO Add 
cmdParser = fmap (const Add) getLine 

data Add = Add deriving Show 
data Entry = Entry String String deriving Show 

Vấn đề là chỉ là promptUsername là một hành động không phải là một String. Hành động 'trả về một chuỗi', vì vậy nó có loại IO String, nhưng chính nó không giống như một chuỗi. Vì Entry x y yêu cầu một số String ở vị trí x, một cái gì đó có hình dạng của một hành động không thể phù hợp hơn so với số hoặc boolean có thể. Vì vậy, trong việc xác định hành động phức tạp của bạn, main, bạn phải 'giải nén' chuỗi đó sẽ là kết quả từ hành động đơn giản promptUsername trong mọi trường hợp thực thi và cung cấp cho đối số đầu tiên là một đối số đầu tiên. Sau đó, bạn thực hiện hành động persist trên kết quả Entry

+0

Cảm ơn! Điểm của bạn cũng được giải thích trong http://learnyouahaskell.com/input-and-output. Nói chung, tôi thấy Tìm hiểu bạn một Haskell cho Great Good! dễ hiểu hơn thế giới thực Haskell (http://book.realworldhaskell.org/). –

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