我迷失在这个概念中。这是我的代码,它的作用就是询问你的名字是什么。
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
放在主要部分
答案 0 :(得分:6)
我们可以想象你要问这个名字是为了打印它,然后让我们改写它 在伪代码中,我们有
main =
print "what is your name"
bind varname with userReponse
print varname
然后你的问题涉及第二条指令 看看这个的语义。
或者如你所知,在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