【问题标题】:Understanding when to use let and <-了解何时使用 let 和 <-
【发布时间】:2014-03-15 23:05:45
【问题描述】:

我已经玩了一段时间 Haskell,但我还没有完全掌握如何使用在 Monad 中运行的第三方函数。每次我回去阅读有关 Monads 等的文章时,我都会得到很好的理解,但是在将它们应用于实际代码时,我无法理解为什么一段代码不起作用。我采用反复试验,通常会编译它,但我觉得我应该能够在第一次正确使用它们而无需尝试通过我的启发式更改(尝试 let, &lt;-, liftM, 等)

所以我想根据这个简单的功能提出几个问题,诚然它做了很多有趣的事情。

import Text.XML.HXT.Core
import Text.HandsomeSoup
import Data.String.Utils

function h = do
    let url  = myUrlBuilder h
        doc  = fromUrl url
        res  = runX $ doc >>> css "strong" /> getText 
        --nres = liftM rmSpaceAndBang (res) 
    res

rmSpaceAndBang ps =  map (\x-> replace "!" "" (strip x))  ps

上面的代码编译。我故意省略了类型声明,因为我认为它应该是不编译的。所以这是我的问题。

为什么我不能这样做 res &lt;- runX ... 并以这种方式返回 res

为什么res 应该在一个 let 语句中,而不是绑定到操作的结果?据我了解,do x &lt;- a1; a2 等同于a1 &gt;&gt;= \x -&gt; a2。当您let x = a1 时,这有什么不同?

当我使用&lt;- 时出现以下错误,如果不是我的反复试验方法,我将无法确定我需要在这里使用let

Couldn't match type `[]' with `IO'
Expected type: IO String
  Actual type: [String]

虽然我专注于上面的res,但我缺乏理解也适用于函数中的其他let 语句。

如何找到res的返回类型?

我想不出一种在 hackage 中搜索 getText 的方法(hxt 似乎太大而无法逐个模块地查看。下次可能会尝试使用 Google 站点搜索)。最后,我最终在 GHCi 中输入了部分代码并输入了:t res。它告诉我它是[String]。有一个更好的方法吗?

由于 res 是 [String] 类型,我想我会将 [String] 作为函数的返回类型。但 GHC 说它应该是IO [String](编译)。为什么:t先给我错误的信息?

当函数返回IO String时,对它们使用纯函数的最佳方式是什么?

现在我被困在IO [String] 里面,我需要用它来提升我进行字符串操作的任何地方。有没有更好的方法来做到这一点?

希望我能从中学到足够的知识,以便能够使用正确的语法,而不必盲目地尝试一些组合。

更新:

我缺少的关键部分是res 不是一个值,而是一个动作。所以我有两个选择:一个是我上面的代码let res =,但最后调用它,另一个是做res &lt;-,然后做return (res)

使用res &lt;- 的优点是我可以摆脱liftM,因为res 现在是[String](请参阅下面@duplode 的答案)。

谢谢!

【问题讨论】:

  • 在Hackage中搜索特定功能,请尝试使用Hayoo:holumbus.fh-wedel.de/hayoo/hayoo.html它也支持签名搜索。
  • 谢谢@DanielDíazCarrete,当我在Hayoo 中做hxt getText 时,马上就找到了这个功能! getText 的返回类型为a XmlTree String。我看到runX 有这种类型IOSArrow XmlTree c -&gt; IO [c]。我现在了解如何获得 IO [String]。
  • let x = a1 ... 非常像 (\x -&gt; ...) a1,而不是 &lt;- 变成的。
  • 你甚至可以不使用res,只需将runX $ doc &gt;&gt;&gt; css "strong" /&gt; getText 放在let 绑定之后。​​

标签: haskell


【解决方案1】:

在您的代码中,resIO [String]。我不怀疑你一开始是通过 GHCi 得到[String],但我相信你用它测试过

>>> res <- runX $ doc >>> css "strong" /> getText
>>> :t res
res :: [String]

这不等同于您的代码。不同之处在于let 只是绑定您的IO [String] 动作而不运行它,而do 块中的&lt;- 运行动作并绑定结果,在本例中为[String] .

现在我被困在 IO [String] 中,我需要用它来提升 我在任何地方都进行字符串操作。有没有更好的方法来做到这一点?

在do块内,有时写起来更方便:

res <- runX $ doc >>> css "strong" /> getText
return $ rmSpaceAndBang res

这完全等同于使用liftM(或fmap):

liftM rmSpaceAndBang $ doc >>> css "strong" /> getText

【讨论】:

  • 感谢您并投了赞成票。我接受了@MdxBhmt 的回答,但您也回答了我的问题。我想这就是在一个线程中提出多个问题的问题。你对我在 GHCi 中所做的事情是正确的,现在我明白了为什么我得到了不同的结果。
【解决方案2】:

为了快速回答, let 不运行任何东西,它只是将 lhs 作为 rhs 的同义词。

您实际上需要在 do 中使用一元函数来执行计算。

  main = do 
     let func = print "I need to be called"
     print "I don't need to be called"
     func

输出:

  "I don't need to be called"
  "I need to be called"

所以你的代码中的res 不是一个值,它是一个单子动作/函数。

请记住,&lt;-&gt;&gt;= 绑定,并且需要在右上方有一个a -&gt; m b

let 没有任何要求。

【讨论】:

  • 谢谢! res 不是一个值,而是一个动作是我缺少的线索。我将使用我的新功能更新我的帖子,以便像我这样的其他人受益。
猜你喜欢
  • 2013-05-07
  • 2019-10-05
  • 2011-07-18
  • 1970-01-01
  • 2016-07-01
  • 2017-03-01
  • 2019-04-26
相关资源
最近更新 更多