【发布时间】:2011-01-19 07:23:15
【问题描述】:
经过多年的 OOP,我正在学习 Haskell。
我正在编写一个功能和状态很少的愚蠢网络蜘蛛。
我不确定如何在 FP 世界中正确地做到这一点。
在 OOP 世界中,这个蜘蛛可以这样设计(按用法):
Browser b = new Browser()
b.goto(“http://www.google.com/”)
String firstLink = b.getLinks()[0]
b.goto(firstLink)
print(b.getHtml())
这段代码加载http://www.google.com/,然后“点击”第一个链接,加载第二页的内容,然后打印内容。
class Browser {
goto(url: String) : void // loads HTML from given URL, blocking
getUrl() : String // returns current URL
getHtml() : String // returns current HTML
getLinks(): [String] // parses current HTML and returns a list of available links (URLs)
private _currentUrl:String
private _currentHtml:String
}
同时拥有 2 个或“浏览器”是可能的,它们有自己独立的状态:
Browser b1 = new Browser()
Browser b2 = new Browser()
b1.goto(“http://www.google.com/”)
b2.goto(“http://www.stackoverflow.com/”)
print(b1.getHtml())
print(b2.getHtml())
问题:展示你如何在 Haskell 中从 scracth 设计这样的东西(类似浏览器的 API,可能有几个独立的实例)?请给一个代码sn-p。
注意:为简单起见,请跳过有关 getLinks() 函数的详细信息(它琐碎且无趣)。
另外假设有一个 API 函数
getUrlContents :: String -> IO String
打开 HTTP 连接并返回给定 URL 的 HTML。
更新:为什么要有状态(或可能没有)?
API 可以有更多的功能,而不仅仅是单一的“加载和解析结果”。
我没有添加它们以避免复杂性。
它还可以关心 HTTP Referer 标头和 cookie,通过在每个请求中发送它们来模拟真实的浏览器行为。
考虑以下场景:
- 打开http://www.google.com/
- 在第一个输入区域输入“haskell”
- 点击“Google 搜索”按钮
- 点击链接“2”
- 点击链接“3”
- 打印当前页面的 HTML(“haskell”的谷歌结果页面 3)
手头有这样的场景,作为开发人员,我希望尽可能地将其转移到代码中:
Browser b = new Browser()
b.goto("http://www.google.com/")
b.typeIntoInput(0, "haskell")
b.clickButton("Google Search") // b.goto(b.finButton("Google Search"))
b.clickLink("2") // b.goto(b.findLink("2"))
b.clickLink("3")
print(b.getHtml())
此场景的目标是在一组操作后获取最后一页的 HTML。 另一个不太明显的目标是保持代码紧凑。
如果浏览器有状态,它可以发送 HTTP Referer 标头和 cookie,同时将所有机制隐藏在自身内部并提供漂亮的 API。
如果浏览器没有状态,开发人员很可能会传递所有当前的 URL/HTML/Cookie ——这会给场景代码增加噪音。
注意:我想有一些库可以在 Haskell 中抓取 HTML,但我的目的不是抓取 HTML,而是了解如何在 Haskell 中正确设计这些“黑盒”的东西。
【问题讨论】: