在Haskell的另一个函数中保持变量 [英] Keep variable inside another function in Haskell

查看:94
本文介绍了在Haskell的另一个函数中保持变量的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我迷失在这个概念中。这是我的代码,它的功能只是询问你的名字。

  askName = do 
name< - getLine
return()

main :: IO()
main = do
putStrLn再次问候,你叫什么名字?
askName

但是,如何在我的 main askName中的变量名称?



这是我第二次尝试:

  askUserName = do 
putStrLn你好,你叫什么名字?
name< - getLine
返回名称

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

main = do
askUserName>> = sayHelloUser

I现在可以以回调方式重新使用名称,但是如果在main中我想再次调用名称,我该怎么做? (避免在主目录中显示 name< - getLine

解决方案

我们可以想象,为了打印它而提出这个名称,然后我们重写它。

在伪代码中,我们有

 main = 
print你的名字是什么
绑定varname和userReponse
print varname

然后你的问题涉及到第二条指令。

看看这个指令的语义。
$ b


  1. userReponse 是一个返回用户输入(getLine)的函数

  2. varname 是一个var

  3. 绑定 var with fun :是一个将var(varname)与一个函数(getLine)的输出相关联的函数。

正如你在Haskell中所知道的那样,一切都是一个函数,那么我们的语义就不太合适。

为了尊重这个习惯用法,我们需要重新审视它。根据后面的反思,我们的 bind 函数的语义变为绑定 fun >



由于我们不能有变量,为了将参数传递给函数,我们需要乍看之下调用另一个函数,以便生成它们。因此,我们需要一种链接两个函数的方法,它正是 bind 应该执行的操作。此外,正如我们的示例所示,评估顺序应该受到尊重,并且这会引导我们进行以下重写 绑定 fun

这表明绑定更像是一个函数,它是一个运算符。

然后,对于所有函数f和g,我们有使用 f 绑定 g。

在haskell中我们注意到了这一点

  f> gt; = g 

另外,我们知道一个函数取0 ,一个或多个参数并返回0,1个或多个参数。

我们可以改进我们的 bind 运算符的定义。

事实上,当f doesn '返回任何结果我们注意>> = >>

应用这些反射到我们的伪代码导致我们

  main = print你叫什么名字>> getLine>> = print 

等一下,绑定运算符与点运算符的用法有何不同为两个功能的组成?

它有很大的不同,因为我们忽略了一个重要的信息,bind并不链接两个函数,但它是链式两个 computations unit 。这就是为什么我们要定义这个操作符的关键。



让我们将一个全局计算写成计算单元的序列。

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

由于这个阶段是一个 strong>可以被定义为计算单元的集合,其中包含两个运算符>> = >>



我们如何在计算机科学中表示 set

通常作为容器

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

因为它是容器,所以我们需要一种将注入值的方法,这是通过返回功能。将一个对象注入到计算中,您可以通过签名来检查它。

  return :: a  - > ma  -  m表示容器,然后全局计算

因为它是一个 strong>我们需要一种方法来管理失败,这由失败函数完成。

实际上,计算的接口由一个class

  class计算
return - 在计算中注入一个对象
>> = - - 链式计算
>> - 链式计算,省略第一个结果
失败 - 管理计算失败



  main :: IO()
main = return什么是你的名字>> =打印>> getLine>> = print

这里我有意包含main函数的签名,我们处于全局 IO计算的事实以及由此产生的输出为()(作为练习在ghci中输入 $:t print )。
如果我们更注重>> = 的定义,我们可以使用下面的语法:

  f>>> = g< => (β>>)和f>> g => f>> =(\_-> g)

然后写

  main :: IO()
main =
返回你叫什么名字>> = \ x - >
print x>> = \_ - >
getLine>> = \ x - >
print x

正如您应该怀疑的那样,我们当然有一个特殊的语法来处理<在计算环境中绑定运算符。你是对的这是 do语法的目的

然后我们以前的代码变成了do语法



<$ p $ main :: IO()
main = do
x< - return你叫什么名字
_< - print x
x< - getLine
print x

如果您想了解更多关于 monad






正如左边提到的那样,我的初步结论有点太热心了你应该感到震惊,因为我们已经打破了参照透明度 law(x在我们的指令序列中取两个不同的值),但它不再重要,因为我们进入了一个计算和一个计算后面定义的是一个容器,我们可以从中导出一个接口,并且该接口被设计为ma nage,作为解释,与现实世界相对应的不纯的世界。

I am lost in this concept. This is my code and what it does is just asking what is your name.

askName = do
  name <- getLine
  return ()

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

However, How can I access in my main the variable name taken in askName?

This is my second attempt:

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

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

main = do
  askUserName >>=  sayHelloUser

I can now re-use the name in a callback way, however if in main I want to call name again, how can I do that? (avoiding to put name <- getLine in the main, obviously)

解决方案

We can imagine that you ask the name in order to print it, then let's rewrite it.
In pseudo code, we have

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

Your question then concern the second instruction.
Take a look about the semantic of this one.

  1. userReponse is a function which return the user input (getLine)
  2. varname is a var
  3. bind var with fun : is a function which associate a var(varname) to the output of a function(getLine)

Or as you know in haskell everything is a function then our semantic is not well suited.
We need to revisit it in order to respect this idiom. According to the later reflexion the semantic of our bind function become bind fun with fun

As we cannot have variable, to pass argument to a function, we need, at a first glance, to call another function, in order to produce them. Thus we need a way to chain two functions, and it's exactly what's bind is supposed to do. Furthermore, as our example suggest, an evaluation order should be respected and this lead us to the following rewriting with fun bind fun

That's suggest that bind is more that a function it's an operator.
Then for all function f and g we have with f bind g.
In haskell we note this as follow

f >>= g  

Furthermore, as we know that a function take 0, 1 or more argument and return 0, 1 or more argument.
We could refine our definition of our bind operator.
In fact when f doesn't return any result we note >>= as >>
Applying, theses reflexions to our pseudo code lead us to

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

Wait a minute, How the bind operator differ from the dot operator use for the composition of two function ?

It's differ a lot, because we have omit an important information, bind doesn't chain two function but it's chain two computations unit. And that's the whole point to understand why we have define this operator.

Let's write down a global computation as a sequence of computation unit.

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

As this stage a global computation could be define as a set of computation unit with two operator >>=, >>.

How do we represent set in computer science ?
Usually as container.

Then a global computation is a container which contain some computation unit. On this container we could define some operator allowing us to move from a computation unit to the next one, taking into account or not the result of the later, this is ours >>= and >> operator.

As it's a container we need a way to inject value into it, this is done by the return function. Which take an object and inject it into a computation, you could check it through is signature.

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

As it's a computation we need a way to manage a failure, this done by the fail function.
In fact the interface of a computation is define by a class

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  

Now we can refine our code as follow

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

Here I have intentionally include the signature of the main function, to express the fact that we are in the global IO computation and the resulting output with be () (as an exercise enter $ :t print in ghci).
If we take more focus on the definition for >>=, we can emerge the following syntax

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

And then write

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

As you should suspect, we certainly have a special syntax to deal with bind operator in computational environment. You're right this is the purpose of do syntax
Then our previous code become, with do syntax

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

If you want to know more take a look on monad


As mentioned by leftaroundabout, my initial conclusion was a bit too enthusiastic

You should be shocked, because we have break referential transparency law (x take two different value inside our sequence of instruction), but it doesn't matter anymore,because we are into a computation, and a computation as defined later is a container from which we can derive an interface and this interface is designed to manage, as explain, the impure world which correspond to the real world.

这篇关于在Haskell的另一个函数中保持变量的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆