Haskell wikibook有一个示例,显示了在尝试在整个数据库中查找不同的连接信息时如何链接lookup
命令,如下所示:
getTaxOwed :: String -- their name
-> Maybe Double -- the amount of tax they owe
getTaxOwed name =
lookup name phonebook >>=
(\number -> lookup number governmentDatabase) >>=
(\registration -> lookup registration taxDatabase)
并以do
表示法重写:
getTaxOwed name = do
number <- lookup name phonebook
registration <- lookup number governmentDatabase
lookup registration taxDatabase
现在,每当我看到一个函数重复不止一次时,我会立即想办法抽象它重复的应用程序,但是因为我还没有在实践中使用Monads
,而且它们似乎已经处于相当高的抽象层次,在这种情况下我不知道如何处理它。
有什么方法,如果有的话,编码员可以抽象出上面的共同模式,即在每一行中调用lookup
?
(旁白:这是“抽象结束”一词的适当背景吗?我觉得它抓住了我的意思,但我不确定,我想确保我正确地使用术语作为相对较新的编码器;我浏览了其他帖子,澄清了它的用法和含义,但我仍然无法弄清楚这个特殊的例子)
答案 0 :(得分:2)
非常感谢Carsten提供foldM
的链接!感谢他们对这个答案的见解。
因此,如果我们使用foldM
,我们可以编写一个函数,该函数通过依赖于每个先前结果的多个目录重复执行lookup
链接。如果,由于使用monads
,在任何时候lookup
都找不到目录中的当前key
,它将终止并返回Nothing
:
lookupALot :: Eq a => a -> [(a,b)] -> Maybe b
lookupALot key directories = foldM lookup key directories
这有
形式的输出 foldM f k1 [d1, d2, ..., dm] -- k == key, d == directory
==
do
k2 <- f k1 d1
k3 <- f k2 d2
...
f km dm
与
完全相同的结构 do
number <- lookup name phonebook
registration <- lookup number governmentDatabase
lookup registration taxDatabase
因此,编写getTaxOwed
的更紧凑方式是:
getTaxOwed :: String -> Maybe Double
getTaxOwed name = foldM lookup name [phonebook, governmentDatabase, taxDatabase]
哪种方式让我感到震惊!该行代码会找到与某个人name
相关联的电话号码,然后查看governmentDatabase
的{{1}}号码,最后查找他们的税务信息。 registration
。但请注意,这仅适用于registration
形式的数据,如[(a,b)]
的类型所示。