将变量保存在Haskell中的另一个函数中

时间:2013-01-07 18:39:28

标签: haskell

我迷失在这个概念中。这是我的代码,它的作用就是询问你的名字是什么。

askName = do
  name <- getLine
  return ()

main :: IO ()
main = do
       putStrLn "Greetings once again.  What is your name?"
       askName

但是,如何在main中访问askName中的变量名?

这是我的第二次尝试:

askUserName = do
  putStrLn "Hello, what's your name?"  
  name <- getLine  
  return name

sayHelloUser name = do
  putStrLn ("Hey " ++ name ++ ", you rock!")

main = do
  askUserName >>=  sayHelloUser

我现在可以以回调的方式重复使用name,但是如果在主要我想再次呼叫名称,我该怎么办呢? (显然,避免将name <- getLine放在主要部分

2 个答案:

答案 0 :(得分:6)

我们可以想象你要问这个名字是为了打印它,然后让我们改写它 在伪代码中,我们有

main =   
    print "what is your name"  
    bind varname with userReponse    
    print varname  

然后你的问题涉及第二条指令 看看这个的语义。

  1. userReponse 是一个返回用户输入的函数(getLine)
  2. varname 是var
  3. 绑定 var 有趣:是一个将var(varname)与输出相关联的函数一个函数(getLine)
  4. 或者如你所知,在haskell中,一切都是函数,那么我们的语义就不太合适了 我们需要重新审视它以尊重这个习语。根据后来的反思,我们的绑定功能的语义变为绑定 有趣 有趣

    由于我们不能有变量,要将参数传递给函数,我们首先需要调用另一个函数,以便生成它们。因此,我们需要一种方法来链接两个函数,而这正是 bind 应该做的事情。此外,正如我们的示例所示,应该尊重评估顺序,这会导致我们进行以下重写 fun bind fun < / em>

    这表明bind更像是一个函数,它是一个运算符 然后对于所有函数f和g,我们有 f bind g。
    在haskell中,我们注意到如下

    f >>= g  
    

    此外,我们知道函数需要0,1个或更多参数并返回0,1个或更多参数 我们可以优化我们对 bind 运算符的定义 事实上,当f不返回任何结果时,我们会将&gt;&gt; = 注意为&gt;&gt;
    应用,这些对我们的伪代码的反思引导我们

    main = print "what's your name" >> getLine >>= print 
    

    等一下,绑定运算符如何区别于点运算符用于两个函数的组合?

    它有很大的不同,因为我们省略了一个重要的信息,绑定不链接两个函数,但它链两个计算单元。这就是理解为什么我们定义这个运算符的重点。

    让我们将全局计算写为计算单元的序列。

    f0 >>= f1 >> f2 >> f3 ... >>= fn
    

    在此阶段,全局计算可以定义为计算单元的,其中包含两个运算符&gt;&gt; = &gt;&gt;

    我们如何在计算机科学中代表 set ? 通常为容器

    然后全局计算是容器,其中包含一些计算单元。在这个容器上我们可以定义一些运算符,允许我们从计算单元移动到下一个,考虑到或不考虑后者的结果,这是我们的&gt;&gt; = &gt;&gt; 运算符。

    由于它是容器,我们需要一种注入值的方法,这是由 return 函数完成的。拿一个对象并将其注入计算,你可以检查它是签名。

    return :: a -> m a -- m symbolize the container, then the global computation
    

    由于这是计算,我们需要一种管理故障的方法,这由失败功能完成。
    事实上,计算的界面是由类

    定义的
    class Computation  
        return  -- inject an objet into a computation 
        >>=     -- chain two computation
        >>      -- chain two computation, omitting the result of the first one
        fail    -- manage a computation failure  
    

    现在我们可以按照以下方式优化我们的代码

    main :: IO ()
    main = return "What's your name" >>= print >> getLine >>= print 
    

    这里我有意包含main函数的签名,以表示我们处于全局 IO计算这一事实,结果输出为()(作为练习,在ghci中输入 $:t print 如果我们更加关注&gt;&gt; = 的定义,我们可以使用以下语法

    f >>= g <=> f >>= (\x -> g)  and f >> g <=> f >>= (\_ -> g)  
    

    然后写

    main :: IO ()
    main =
        return "what's your name" >>= \x ->
        print x                   >>= \_ -> 
        getLine                   >>= \x -> 
        print x 
    

    正如您应该怀疑的那样,我们当然有一种特殊的语法来处理计算环境中的 bind 运算符。你是对的,这是做语法的目的 然后我们以前的代码变成了,用do语法

    main :: IO ()
    main = do
        x <- return "what's your name"
        _ <- print x  
        x <- getLine              
        print x     
    

    如果您想了解更多,请查看monad


    正如左下图所述,我的初步结论有点过于热情

    你应该感到震惊,因为我们打破了引用透明度法则(x在我们的指令序列中取两个不同的值),但它不再重要,因为我们进入了一个计算,后面定义的计算是一个容器,我们可以从中导出一个接口,该接口旨在管理<解释<强大的>不纯的与现实世界相对应的世界。

答案 1 :(得分:3)

从askname返回名称。在Haskell中,访问“全局”变量不是惯用的:

askName :: IO String
askName = do
  name <- getLine
  return name

main :: IO ()
main = do
       putStrLn "Greetings once again.  What is your name?"
       name <- askName
       putStrLn name

现在唯一的问题是askName函数有点无意义,因为它现在只是getLine的别名。我们可以通过将问题放在askName:

中来“修复”
askName :: IO String
askName = do
  putStrLn "Greetings once again.  What is your name?"
  name <- getLine
  return name

main :: IO ()
main = do
       name <- askName
       putStrLn name

最后,只有两个小问题:在学习时放置类型声明通常是一个好主意,使事情明确并帮助编译错误消息。另一件事是要记住“返回”函数仅用于monadic代码(类似于传统的return语句!)有时候我们可以省略一些中间变量:

askName :: IO String
askName = do
  putStrLn "Greetings once again.  What is your name?"
  getLine