1

好的,所以我是 Haskell IO 的新手。我已经阅读了很多关于 Haskell 函数中的 IO 和副作用的内容,现在我已经开始在 Haskell 中做一些我自己的副作用,我想知道 - 我该如何编写这些东西?

我有以下功能,因此在运行其中一行代码后,我想做一些打印,前几行的注释对此进行了解释。

我很确定我需要更改函数的类型签名,也许我需要使用 Maybe。也许甚至不可能这样做,我必须完全重写它?我真的不知道 - 但我正在寻找指导。我如何着手包含此功能?

interpret_statement :: Prog -> Vars -> Stmt -> Vars -- one third of the debug -d functionality goes here 
                                                    -- AFTER every assignment is executed, the interpreter should print a line specifying
                                                    -- the variable being assigned to AND its new value
interpret_statement prog vars@(Vars _ b c d) (Assign A expr) = Vars (interpret_expr prog vars expr) b c d
interpret_statement prog vars@(Vars a _ c d) (Assign B expr) = Vars a (interpret_expr prog vars expr) c d
interpret_statement prog vars@(Vars a b _ d) (Assign C expr) = Vars a b (interpret_expr prog vars expr) d
interpret_statement prog vars@(Vars a b c _) (Assign D expr) = Vars a b c (interpret_expr prog vars expr)
4

2 回答 2

5

我将从 Adrian 答案中的代码开始。

interpret_statement :: Prog -> Vars -> Stmt -> IO Vars
interpret_statement prog vars@(Vars _ b c d) (Assign A expr) = do
    print "some debug"
    return $ Vars (interpret_expr prog vars expr) b c d
-- etc

就目前而言,这是正确的,但存在一些问题:

  • 调试输出是强制性的,但大概你希望它是可选的
  • 尽管我们需要结果类型IO Vars才能执行 i/o,但这使得测试变得更加困难interpret_statement

一个解法:

  • 添加一个函数参数来interpret_statement执行任何所需的 i/o
  • 使该函数具有多态性,以便其结果类型确定interpret_statement

例如(请原谅我猜测您的类型:我假设data VarName = A | B | C | Ddata Vars = Vars Value Value Value Value):

interpret_statement :: (VarName -> Value -> Vars -> o) ->
                       Prog -> Vars -> Stmt -> o
interpret_statement debug prog vars@(Vars _ b c d) (Assign A expr)
    = debug A newValue $ Vars (interpret_expr prog vars expr) b c d
-- etc

您可以作为该参数提供的有用功能是:

purePassthrough :: VarName -> Value -> Vars -> Vars
purePassthrough _ _ vars = vars
  • 当您interpret_statement从 QuickCheck 进行测试并希望它是纯的时使用
  • interpret_statement purePassthrough :: Prog -> Vars -> Stmt -> Vars,就像你原来的一样interpret_statement
writeDebuggingInfo :: VarName -> Value -> Vars -> IO Vars
writeDebuggingInfo varName newValue newVars = do
    putStrLn $ show varName ++ " := " ++ show newValue
        -- or whatever debugging output you want
    return newVars
  • 当您需要编写调试信息时在您的程序中使用
  • interpret_statement writeDebuggingInfo :: Prog -> Vars -> Stmt -> IO Vars,正如阿德里安的回答
dontWriteDebuggingInfo :: VarName -> Value -> Vars -> IO Vars
dontWriteDebuggingInfo :: _ _ newVars = return newVars
  • interpret_statement dontWriteDebuggingInfo :: Prog -> Vars -> Stmt -> IO Vars, 和前面的情况一样
  • 在您可能需要也可能不需要调试输出时使用,例如

    newVars <- interpret_statement (if wantDebuggingOutput then writeDebuggingInfo
                                                      else dontWriteDebuggingInfo)
                   program vars statement
    
于 2013-04-14T10:05:48.207 回答
4

You are correct. If you want to do some printing your code must live in some monad. Monad responsible for handling operations on real world - like printing on screen - is IO monad. So to use functions like putStrLn, print, getLine you need to change function signature to:

interpret_statement :: Prog -> Vars -> Stmt -> IO Vars

And use do notation. Result of the function must be then wrapped by return funciton, but I am sure you already know this about monads.

For example:

interpret_statement prog vars@(Vars _ b c d) (Assign A expr) = do
    print "some debug"
    return $ Vars (interpret_expr prog vars expr) b c d
于 2013-04-14T07:31:27.617 回答