Haskell中的副作用和I / O.

时间:2013-04-14 07:21:30

标签: haskell

好的,我是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)

2 个答案:

答案 0 :(得分: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

解决方案:

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

e.g。 (原谅我猜测你的类型:我假设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
  • 从QuickCheck测试interpret_statement时使用 并希望它是纯粹的
  • 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
    

答案 1 :(得分:4)

你是对的。如果你想做一些打印,你的代码必须存在于一些monad中。 Monad负责处理现实世界的操作 - 比如在屏幕上打印 - 是IO monad。因此,要使用putStrLn, print, getLine之类的函数,您需要将函数签名更改为:

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

并使用do表示法。函数的结果必须由return函数包装,但我相信你已经知道了monad。

例如:

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