2013-05-21 33 views
5

Trong quá trình viết bài tập cho trường đại học, tôi đang có niềm vui vui vẻ khi học hỏi các thầy Haskell mới. Yay !!!Unwrapping Mono Haskell State

Tôi có một chức năng mà typechecks tốt:

compile :: Prog -> State VarsState String 
compile [email protected](Prog functions) = do 
    s1 <- sequence (map (translate_func 0) [get_function prog name | name <- [func_name func | func <- functions]]) 
    return $ trace ("here's the program: \n" ++ show prog) $ concat $ s1 

nhưng khi chức năng khác này:

maybe_compile_prog :: 
    MaybeOK Prog -> String -> IO() 
maybe_compile_prog (Error msg) _ = do 
    putStrLn ("error: " ++ msg) 
maybe_compile_prog (OK prog) modulename = do 
    s1 <- compile prog 
    writeFile (modulename ++ ".m") ((header modulename) ++ s1) 

cố gắng để gọi nó, nó thổi lên tại dòng

s1 <- compile prog 

nói rằng nó không thể phù hợp với loại dự kiến ​​"IO t0" với loại thực tế "State VarsState String".

Tôi giả định điều này là bởi vì maybe_compile_prog trả về kiểu IO() vì vậy nó dự kiến ​​sẽ chỉ unwrap thông tin IO? VarsState là kiểu dữ liệu tùy chỉnh mà tôi đã tạo để sử dụng với tiểu đơn vị/

Tuy nhiên, nếu đó là vấn đề và tôi cho rằng đó là, tôi không biết cách truyền chuỗi đơn giản này tới may_compile_prog. Thực sự, đó là tất cả những gì tôi muốn làm - cho một chuỗi ký tự có thể_compile_prog.

Có thể có một số cách gọn gàng để gỡ bỏ đơn nguyên trạng thái này? Có lẽ nó có thể viết lại "biên dịch" để nó có trong một số thông tin đơn nguyên trạng thái trong khi nó chạy, nhưng sau đó chỉ trả về một chuỗi (không được bọc trong bất kỳ đơn nguyên nào)?

Vui lòng cho tôi biết nếu tôi thiếu thông tin.

Trả lời

11

compile prog là một hành động trong đơn lẻ State VarsState, do đó bạn không thể sử dụng nó trong số IO -do-block như vậy. Trong một khối lệnh, tất cả các dòng phải sử dụng cùng một đơn nguyên, trong trường hợp này là IO.

Bạn cần phải "chạy" các hành động compile để có được kết quả, với một trong những

runState :: State s a -> s -> (a,s) 
evalState :: State s a -> s -> a 
execState :: State s a -> s -> s 

tùy thuộc vào việc bạn cần

  • cả hai, kết quả và trạng thái cuối cùng
  • chỉ dẫn
  • chỉ trạng thái cuối cùng

Trong trường hợp của bạn, bạn chỉ muốn kết quả, do đó, evalState nó là.

Cho rằng bạn cần phải cung cấp một trạng thái ban đầu, nó có thể trông giống như

maybe_compile_prog (OK prog) modulename = do 
    let s1 = evalState (compile prog) initialState 
    writeFile (modulename ++ ".m") ((header modulename) ++ s1) 

nhưng sau đó tình trạng ban đầu được cung cấp cho hành động compile sẽ là như nhau cho tất cả OK prog s trôi qua. Nếu đó không phải là điều đúng, bạn có thể vượt qua trạng thái ban đầu như một tham số.

+1

Anh chàng hoàn hảo. Ngoại trừ một điều nhỏ, nó biên dịch với "let s1 = evalState (compile prog) initialState", không phải "let s1 = evalState $ compile prog initialState". Một câu hỏi khác - có những chức năng như thế này cho các monads khác không? tức là các chức năng mà không làm đơn lẻ và trả lại cho bạn kết quả? – nebffa

+0

@nebffa phụ thuộc hoàn toàn vào đơn nguyên trong câu hỏi, nhưng có, một số người trong số họ làm.Nó không phải là một cái gì đó bạn có thể làm chung cho tất cả các monads. Một ví dụ khác sẽ là 'unsafePerformIO' đáng sợ đối với đơn nguyên 'IO', nhưng bạn không nên sử dụng nó. –

+0

Ah, vâng, trộn lẫn các dấu ngoặc đơn (quên trạng thái trước, rồi quên trao đổi '$' khi thêm nó). Cảm ơn cho những người đứng đầu lên. Đối với nhiều monads, có các hàm tương ứng, nhưng không phải cho tất cả. 'IO' là một trong những nơi không có cái nào như vậy [tốt, có một cái tên bắt đầu với 'không an toàn' và nó thực sự thực sự có nghĩa là nó]. Có 'runIdentity',' runReader', 'runWriter',' runCont', ... Nhìn vào tài liệu cho mỗi đơn nguyên liệu có các hàm như vậy hay không. –