简体   繁体   English

Haskell的副作用和I / O

[英]Side effects and I/O in Haskell

Ok so I'm a newbie to Haskell IO. 好的,我是Haskell IO的新手。 I've read a lot about IO and side effects in Haskell functions, and now I have come to do some side effects of my own in Haskell and I'm wondering - how do I actually write this stuff? 我已经阅读了很多有关Haskell函数中的IO和副作用的信息,现在我开始在Haskell中做一些自己的副作用,我想知道-我该如何实际编写这些东西?

I have the following function, whereupon after running one of the lines of code, I want to do some printing, which is explained by the comments in the first couple of lines. 我具有以下功能,因此在运行其中一行代码之后,我想进行一些打印,这在前两行的注释中得到了解释。

I'm pretty sure I need to change the function's type signature, maybe I'll need to use Maybe. 我很确定我需要更改函数的类型签名,也许我需要使用Maybe。 Maybe it's not even possible to do this way and I have to completely rewrite it? 也许不可能这样做,而我必须完全重写它? I don't really know - but I'm looking for guidance. 我真的不知道-但是我正在寻找指导。 How do I go about including this functionality? 我如何才能包括此功能?

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)

I'm going to start with the code from Adrian's answer. 我将从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

This is correct as far as it goes, but there are some issues: 就目前而言,这是正确的,但是存在一些问题:

  • the debugging output is compulsory, but presumably you want it to be optional 调试输出是强制性的,但是大概您希望它是可选的
  • although we need the result type to be IO Vars in order to perform i/o, this makes it harder to test interpret_statement 尽管我们需要将结果类型设置为IO Vars才能执行I / O,但这使测试interpret_statement更加困难

A solution: 一个解法:

  • add a function parameter to interpret_statement that performs any required i/o 向执行任何必需的I / O的interpret_statement声明添加函数参数
  • make that function polymorphic so that its result type determines the result type of interpret_statement 使该函数变为多态,以便其结果类型确定interpret_statement的结果类型

eg (and forgive me for guessing at your types: I assume data VarName = A | B | C | D and data Vars = Vars Value Value Value Value ): 例如(请原谅我猜测您的类型:我假设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

Useful functions you can provide as that parameter are: 您可以作为该参数提供的有用功能是:

purePassthrough :: VarName -> Value -> Vars -> Vars
purePassthrough _ _ vars = vars
  • use when you are testing interpret_statement from QuickCheck and would like it to be pure 当您从QuickCheck测试interpret_statement并希望它是纯净的时使用
  • interpret_statement purePassthrough :: Prog -> Vars -> Stmt -> Vars , as in your original 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
  • use in your program when you require debugging information to be written 需要编写调试信息时在程序中使用
  • interpret_statement writeDebuggingInfo :: Prog -> Vars -> Stmt -> IO Vars , as in Adrian's answer interpret_statement writeDebuggingInfo :: Prog -> Vars -> Stmt -> IO Vars ,如Adrian的回答
dontWriteDebuggingInfo :: VarName -> Value -> Vars -> IO Vars
dontWriteDebuggingInfo :: _ _ newVars = return newVars
  • interpret_statement dontWriteDebuggingInfo :: Prog -> Vars -> Stmt -> IO Vars , as in the previous case interpret_statement dontWriteDebuggingInfo :: Prog -> Vars -> Stmt -> IO Vars ,如前一种情况
  • use when you may or may not want debugging output, eg 在您可能需要调试输出或不希望调试输出时使用,例如

     newVars <- interpret_statement (if wantDebuggingOutput then writeDebuggingInfo else dontWriteDebuggingInfo) program vars statement 

You are correct. 你是对的。 If you want to do some printing your code must live in some monad. 如果要打印,您的代码必须位于某个monad中。 Monad responsible for handling operations on real world - like printing on screen - is IO monad. 负责处理现实世界中操作(例如在屏幕上打印)的Monad是IO monad。 So to use functions like putStrLn, print, getLine you need to change function signature to: 因此,要使用putStrLn, print, getLine类的putStrLn, print, getLine您需要将函数签名更改为:

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

And use do notation. 和使用do记号。 Result of the function must be then wrapped by return funciton, but I am sure you already know this about monads. 然后,必须通过return函数包装函数的结果,但是我确定您已经知道有关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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM